diff --git a/docs/module/ECMAddCompilerFlag.rst b/docs/module/ECMAddCompilerFlag.rst
new file mode 100644
index 0000000..320a51a
--- /dev/null
+++ b/docs/module/ECMAddCompilerFlag.rst
@@ -0,0 +1 @@
+.. ecm-module:: ../../modules/ECMAddCompilerFlag.cmake
diff --git a/kde-modules/KDECompilerSettings.cmake b/kde-modules/KDECompilerSettings.cmake
index 6e5193e..b60e28d 100644
--- a/kde-modules/KDECompilerSettings.cmake
+++ b/kde-modules/KDECompilerSettings.cmake
@@ -182,6 +182,8 @@ endif()
 # Language and toolchain features
 ############################################################
 
+include(ECMAddCompilerFlag)
+
 # Pick sensible versions of the C and C++ standards.
 if (NOT CMAKE_C_STANDARD)
     set(CMAKE_C_STANDARD 90)
@@ -404,20 +406,14 @@ if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES "Clang"
     # Make some warnings errors
     set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror=return-type")
 endif()
-if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR
-    (CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 3.5))
-    # -Wvla: use of variable-length arrays (an extension to C++)
-    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wvla")
-endif()
-if ((CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.0) OR
-    (CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 3.5))
-    include(CheckCXXCompilerFlag)
-    check_cxx_compiler_flag(-Wdate-time HAVE_DATE_TIME)
-    if (HAVE_DATE_TIME)
-        # -Wdate-time: warn if we use __DATE__ or __TIME__ (we want to be able to reproduce the exact same binary)
-        set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wdate-time")
-    endif()
-endif()
+ecm_add_cxx_compiler_flags_if_supported(FLAGS -Wvla
+    SUPPORTED_IF CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR
+        (CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 3.5)
+    QUERY_IF APPLE)
+ecm_add_cxx_compiler_flags_if_supported(FLAGS -Wdate-time
+    SUPPORTED_IF (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.0) OR
+        (CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 3.5)
+    QUERY_IF APPLE)
 
 if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
    if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS "5.0.0")
@@ -527,12 +523,13 @@ if (MINGW AND CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
     set (CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -Wl,--export-all-symbols")
 endif()
 
-if (CMAKE_GENERATOR STREQUAL "Ninja" AND
-    ((CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.9) OR
-     (CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 3.5)))
+if (CMAKE_GENERATOR STREQUAL "Ninja")
     # Force colored warnings in Ninja's output, if the compiler has -fdiagnostics-color support.
     # Rationale in https://github.com/ninja-build/ninja/issues/814
-    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fdiagnostics-color=always")
+    ecm_add_cxx_compiler_flags_if_supported(FLAGS -fdiagnostics-color=always
+        SUPPORTED_IF ((CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.9) OR
+            (CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 3.5))
+        QUERY_IF APPLE)
 endif()
 
 include("${ECM_MODULE_DIR}/ECMEnableSanitizers.cmake")
diff --git a/kde-modules/KDEFrameworkCompilerSettings.cmake b/kde-modules/KDEFrameworkCompilerSettings.cmake
index 27298b8..74c088e 100644
--- a/kde-modules/KDEFrameworkCompilerSettings.cmake
+++ b/kde-modules/KDEFrameworkCompilerSettings.cmake
@@ -49,18 +49,9 @@ add_definitions(
     -DKF_DEPRECATED_WARNINGS_SINCE=0x060000
 )
 
-if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
-   set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pedantic")
-endif()
-
-if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
-   if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS "5.0.0")
-      set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wzero-as-null-pointer-constant" )
-   endif()
-endif()
+ecm_add_cxx_compiler_flags_if_supported(FLAGS -pedantic
+    SUPPORTED_IF CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
 
-if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
-   if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS "5.0.0")
-      set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wzero-as-null-pointer-constant" )
-   endif()
-endif()
+ecm_add_cxx_compiler_flags_if_supported(FLAGS -Wzero-as-null-pointer-constant
+    SUPPORTED_IF CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS "5.0.0"
+    QUERY_IF CMAKE_CXX_COMPILER_ID MATCHES "Clang")
diff --git a/modules/ECMAddCompilerFlag.cmake b/modules/ECMAddCompilerFlag.cmake
new file mode 100644
index 0000000..ef216c4
--- /dev/null
+++ b/modules/ECMAddCompilerFlag.cmake
@@ -0,0 +1,153 @@
+#.rst:
+# ECMAddCompilerFlag
+# ------------------
+#
+# Add compiler one or more flag conditionally
+#
+# ::
+#
+#  ECM_ADD_CXX_COMPILER_FLAGS_IF_SUPPORTED(FLAGS <flag|flags>
+#     [SUPPORTED_IF <support_condition>]
+#     [QUERY_IF <query_condition>])
+#
+# C-language equivalent::
+#
+#  ECM_ADD_C_COMPILER_FLAGS_IF_SUPPORTED(FLAGS <flag|flags>
+#     [SUPPORTED_IF <support_condition>]
+#     [QUERY_IF <query_condition>])
+#
+# add ``<flag>`` or ``<flags>`` to CMAKE_CXX_FLAGS if the compiler supports them.
+# Support is determined by the ``SUPPORTED_IF`` expression if provided or by
+# querying the compiler directly if a ``QUERY_IF`` expression is true. The
+# ``QUERY_IF`` expression takes precedence if it evaluates to true.
+# The compiler is also queried if no conditions are given at all.
+# The compiler is queried for each flag in turn and only the supported
+# flag(s) are added. The ``SUPPORTED_IF`` condition applies to all flags.
+#
+# examples:
+#
+# add flags -a, -b and -c when using a GCC compiler or Clang:
+#
+# .. code-block:: cmake
+#
+#   ecm_add_cxx_compiler_flags_if_supported(FLAGS -a -b -c SUPPORTED_IF CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
+#
+# add flags -d, -e and/or -f if the compiler supports them:
+#
+# .. code-block:: cmake
+#
+#   ecm_add_cxx_compiler_flags_if_supported(FLAGS -d -e -f)
+#
+# add flag -a when using a GCC > 5 compiler, or query the compiler when running on a Mac
+# (a more specific test would be APPLE AND CMAKE_CXX_COMPILER_ID MATCHES "Clang" but there is
+# little chance another compiler will be used on that platform):
+#
+# .. code-block:: cmake
+#
+#   ecm_add_c_compiler_flags_if_supported(FLAGS -g
+#     SUPPORTED_IF CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS "5.0.0"
+#     QUERY_IF APPLE)
+#
+# Since 5.xx
+
+#=============================================================================
+# Copyright 2018,9      René J.V. Bertin <rjvbertin@gmail.com>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the copyright
+#    notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the copyright
+#    notice, this list of conditions and the following disclaimer in the
+#    documentation and/or other materials provided with the distribution.
+# 3. The name of the author may not be used to endorse or promote products
+#    derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+include(CMakeParseArguments)
+include(CheckCXXCompilerFlag)
+include(CheckCCompilerFlag)
+
+
+function(ECM_ADD_CXX_COMPILER_FLAGS_IF_SUPPORTED)
+    set(_OPTIONS_ARGS)
+    set(_ONE_VALUE_ARGS)
+    set(_MULTI_VALUE_ARGS FLAGS SUPPORTED_IF QUERY_IF)
+
+    cmake_parse_arguments(EASCXXFLAGS "${_OPTIONS_ARGS}" "${_ONE_VALUE_ARGS}" "${_MULTI_VALUE_ARGS}" ${ARGN})
+    if(NOT EASCXXFLAGS_FLAGS)
+        message(FATAL_ERROR "ecm_add_cxx_compiler_flags_if_supported: 'FLAGS' is a required argument.")
+    endif()
+    # if the user provided conditions, evaluate them now to simplify things later
+    if(EASCXXFLAGS_SUPPORTED_IF AND (${EASCXXFLAGS_SUPPORTED_IF}))
+        set(EASCXXFLAGS_is_supported ON)
+    endif()
+    if((EASCXXFLAGS_QUERY_IF AND (${EASCXXFLAGS_QUERY_IF}))
+        OR (NOT EASCXXFLAGS_SUPPORTED_IF AND NOT EASCXXFLAGS_QUERY_IF))
+        set(EASCXXFLAGS_needs_query ON)
+    endif()
+    if(EASCXXFLAGS_needs_query)
+        # without conditions, or when QUERY_IF is true we'll need to ask the compiler directly.
+        # one flag at a time:
+        set(flags)
+        foreach(flag IN ITEMS ${EASCXXFLAGS_FLAGS})
+            # use a standardised and informative cached test variable
+            set(HASFLAG "${CMAKE_CXX_COMPILER_ID}++_ACCEPTS${flag}")
+            check_cxx_compiler_flag(${flag} ${HASFLAG})
+            if(${${HASFLAG}})
+                set(flags "${flags} ${flag}")
+            endif()
+        endforeach()
+        set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${flags}" PARENT_SCOPE)
+    elseif(EASCXXFLAGS_is_supported)
+        # all flags can be appended at once
+        string(REPLACE ";" " " FLAGS "${EASCXXFLAGS_FLAGS}")
+        set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${FLAGS}" PARENT_SCOPE)
+    endif()
+endfunction()
+
+function(ECM_ADD_C_COMPILER_FLAGS_IF_SUPPORTED)
+    set(_OPTIONS_ARGS)
+    set(_ONE_VALUE_ARGS)
+    set(_MULTI_VALUE_ARGS FLAGS SUPPORTED_IF QUERY_IF)
+
+    cmake_parse_arguments(EASCFLAGS "${_OPTIONS_ARGS}" "${_ONE_VALUE_ARGS}" "${_MULTI_VALUE_ARGS}" ${ARGN})
+    if(NOT EASCFLAGS_FLAGS)
+        message(FATAL_ERROR "ecm_add_c_compiler_flags_if_supported: 'FLAGS' is a required argument.")
+    endif()
+    # if the user provided conditions, evaluate them now to simplify things later
+    if(EASCFLAGS_SUPPORTED_IF AND (${EASCFLAGS_SUPPORTED_IF}))
+        set(EASCFLAGS_is_supported ON)
+    endif()
+    if((EASCFLAGS_QUERY_IF AND (${EASCFLAGS_QUERY_IF}))
+        OR (NOT EASCFLAGS_SUPPORTED_IF AND NOT EASCFLAGS_QUERY_IF))
+        set(EASCFLAGS_needs_query ON)
+    endif()
+    if(EASCFLAGS_needs_query)
+        set(flags)
+        foreach(flag IN ITEMS ${EASCFLAGS_FLAGS})
+            set(HASFLAG "${CMAKE_C_COMPILER_ID}_ACCEPTS${flag}")
+            check_c_compiler_flag(${flag} ${HASFLAG})
+            if(${${HASFLAG}})
+                set(flags "${flags} ${flag}")
+            endif()
+        endforeach()
+        set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${flags}" PARENT_SCOPE)
+    elseif(EASCFLAGS_is_supported)
+        string(REPLACE ";" " " FLAGS "${EASCFLAGS_FLAGS}")
+        set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${FLAGS}" PARENT_SCOPE)
+    endif()
+endfunction()
+
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 1901ca1..ed43650 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -224,3 +224,5 @@ set(ECMGenerateDBusServiceFileTest_EXTRA_OPTIONS
 add_test_macro(ECMGenerateDBusServiceFileTest
     ${CMAKE_COMMAND} -P "${CMAKE_CURRENT_BINARY_DIR}/ECMGenerateDBusServiceFileTest/check_tree.cmake"
 )
+
+add_test_macro(ECMAddCompilerFlag dummy)
diff --git a/tests/ECMAddCompilerFlag/CMakeLists.txt b/tests/ECMAddCompilerFlag/CMakeLists.txt
new file mode 100644
index 0000000..688e057
--- /dev/null
+++ b/tests/ECMAddCompilerFlag/CMakeLists.txt
@@ -0,0 +1,115 @@
+project(ECMAddCompilerFlagTest)
+cmake_minimum_required(VERSION 2.8.12)
+
+set(CMAKE_MODULE_PATH
+    ${CMAKE_CURRENT_SOURCE_DIR}/../../modules
+    ${CMAKE_CURRENT_SOURCE_DIR}/../../find-modules
+)
+
+include(ECMAddCompilerFlag)
+
+### test a standard C++ option supported by GCC and Clang
+set(TESTFLAG "-fexceptions")
+### clang-only flags
+set(TESTFLAG2 "-fblocks")
+set(TESTFLAG3 "-fcxx-exceptions")
+# a bogus flag
+set(TESTFLAG4 "-bogus-drapeau")
+
+set(CMAKE_CXX_FLAGS "")
+
+ecm_add_cxx_compiler_flags_if_supported(FLAGS ${TESTFLAG}
+    SUPPORTED_IF CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
+
+ecm_add_cxx_compiler_flags_if_supported(FLAGS ${TESTFLAG2} ${TESTFLAG3}
+    QUERY_IF CMAKE_CXX_COMPILER_ID MATCHES "Clang")
+
+# no conditional expression means always query the compiler
+ecm_add_cxx_compiler_flags_if_supported(FLAGS ${TESTFLAG4})
+
+message(STATUS "CMAKE_CXX_FLAGS=${CMAKE_CXX_FLAGS}")
+
+if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
+    if(NOT CMAKE_CXX_FLAGS MATCHES "${TESTFLAG}")
+        message(FATAL_ERROR "${TESTFLAG} should be set for ${CMAKE_CXX_COMPILER_ID}")
+    endif()
+else()
+    if(CMAKE_CXX_FLAGS MATCHES "${TESTFLAG}")
+        message(FATAL_ERROR "${TESTFLAG} should not be set for ${CMAKE_CXX_COMPILER_ID}")
+    endif()
+endif()
+
+
+if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
+    if(NOT CMAKE_CXX_FLAGS MATCHES "${TESTFLAG2}")
+        message(FATAL_ERROR "${TESTFLAG2} should be set for ${CMAKE_CXX_COMPILER_ID}")
+    endif()
+    if(NOT CMAKE_CXX_FLAGS MATCHES "${TESTFLAG3}")
+        message(FATAL_ERROR "${TESTFLAG3} should be set for ${CMAKE_CXX_COMPILER_ID}")
+    endif()
+else()
+    if(CMAKE_CXX_FLAGS MATCHES "${TESTFLAG2}")
+        message(FATAL_ERROR "${TESTFLAG2} should not be set for ${CMAKE_CXX_COMPILER_ID}")
+    endif()
+    if(CMAKE_CXX_FLAGS MATCHES "${TESTFLAG3}")
+        message(FATAL_ERROR "${TESTFLAG3} should not be set for ${CMAKE_CXX_COMPILER_ID}")
+    endif()
+endif()
+
+
+if(CMAKE_CXX_FLAGS MATCHES "${TESTFLAG4}")
+    message(FATAL_ERROR "${TESTFLAG4} should not be set for ${CMAKE_CXX_COMPILER_ID}")
+endif()
+
+### test if -pedantic is added correctly for GCC or Clang
+set(TESTFLAG "-pedantic")
+### clang-only flags
+set(TESTFLAG2 "-fblocks")
+set(TESTFLAG3 "-Xclang -Wall")
+
+set(CMAKE_C_FLAGS "")
+
+ecm_add_c_compiler_flags_if_supported(FLAGS ${TESTFLAG}
+    SUPPORTED_IF CMAKE_C_COMPILER_ID STREQUAL "GNU" OR CMAKE_C_COMPILER_ID MATCHES "Clang")
+
+ecm_add_c_compiler_flags_if_supported(FLAGS ${TESTFLAG2} ${TESTFLAG3}
+    QUERY_IF CMAKE_C_COMPILER_ID MATCHES "Clang")
+
+# no conditional expression means always query the compiler
+ecm_add_c_compiler_flags_if_supported(FLAGS ${TESTFLAG4})
+
+message(STATUS "CMAKE_C_FLAGS=${CMAKE_C_FLAGS}")
+
+if(CMAKE_C_COMPILER_ID STREQUAL "GNU" OR CMAKE_C_COMPILER_ID MATCHES "Clang")
+    if(NOT CMAKE_C_FLAGS MATCHES "${TESTFLAG}")
+        message(FATAL_ERROR "${TESTFLAG} should be set for ${CMAKE_C_COMPILER_ID}")
+    endif()
+else()
+    if(CMAKE_C_FLAGS MATCHES "${TESTFLAG}")
+        message(FATAL_ERROR "${TESTFLAG} should not be set for ${CMAKE_C_COMPILER_ID}")
+    endif()
+endif()
+
+
+if(CMAKE_C_COMPILER_ID MATCHES "Clang")
+    if(NOT CMAKE_C_FLAGS MATCHES "${TESTFLAG2}")
+        message(FATAL_ERROR "${TESTFLAG2} should be set for ${CMAKE_C_COMPILER_ID}")
+    endif()
+    if(NOT CMAKE_C_FLAGS MATCHES "${TESTFLAG3}")
+        message(FATAL_ERROR "${TESTFLAG3} should be set for ${CMAKE_C_COMPILER_ID}")
+    endif()
+else()
+    if(CMAKE_C_FLAGS MATCHES "${TESTFLAG2}")
+        message(FATAL_ERROR "${TESTFLAG2} should not be set for ${CMAKE_C_COMPILER_ID}")
+    endif()
+    if(CMAKE_C_FLAGS MATCHES "${TESTFLAG3}")
+        message(FATAL_ERROR "${TESTFLAG3} should not be set for ${CMAKE_C_COMPILER_ID}")
+    endif()
+endif()
+
+
+if(CMAKE_C_FLAGS MATCHES "${TESTFLAG4}")
+    message(FATAL_ERROR "${TESTFLAG4} should not be set for ${CMAKE_C_COMPILER_ID}")
+endif()
+
+add_executable(dummy main.c)
diff --git a/tests/ECMAddCompilerFlag/main.c b/tests/ECMAddCompilerFlag/main.c
new file mode 100644
index 0000000..c13815c
--- /dev/null
+++ b/tests/ECMAddCompilerFlag/main.c
@@ -0,0 +1,4 @@
+int main()
+{
+   return 0;
+}
