I'm trying to introduce -Werror flag to rather big legacy project. As expected, it breaks the compilation completely. Therefore I've decided to introduce it gradually, and for the new code first of all. My original approach was to compile new features as separate static targets and link them to the project, which works kind of good both in terms of project structure and readability. The problem which persist are pre-existing tangled includes. Basically, even after fixing all warnings in new code I'm left with chain of includes introducing new warnings. Is there any way to limit warning flags usage to given source files strictly?
I do understand that include means basically copy/pasting headers into cpps, so it does not seem possible with just cmake settings. Then pragmas, perhaps?
You can use the set_source_files_properties command to set the COMPILE_OPTIONS property on the files you need.
Something like this:
set_source_files_properties(bad.cpp PROPERTIES COMPILE_OPTIONS -Werror)
I don't know about any compiler flags that allow you to apply flags to only some of the files included, so cmake cannot do better for you. Therefore pragmas are the way to go.
Basically what you effectively want in your cpp files is something like this:
#pragma GCC diagnostic push
#pragma GCC diagnostic error "-Wall"
#include "updated_lib/header1.hpp"
#include "updated_lib/header2.hpp"
...
#pragma GCC diagnostic pop
#include "non_updated_lib/header1.hpp"
#include "non_updated_lib/header2.hpp"
Note that this would require this logic to be repeated in multiple translation units which you may want to avoid, if you're updating the headers gradually over time.
As an alternative you could duplicate the header file subdirectories, and make the non-updated versions available via one path and updated headers via another, e.g. for a header foo/bar/baz.hpp either make the header available via path old/foo/bar/baz.hpp or new/foo/bar/baz.hpp and create a new header available via foo/bar/baz.hpp that looks like this:
#if __has_include("new/foo/bar/baz.hpp")
# pragma GCC diagnostic push
# pragma GCC diagnostic error "-Wall"
# include "new/foo/bar/baz.hpp"
# pragma GCC diagnostic pop
#else
# pragma GCC diagnostic push
# pragma GCC diagnostic warning "-Wall"
# include "old/foo/bar/baz.hpp"
# pragma GCC diagnostic pop
#endif
Note that you'll probably need to write these kind of headers for you. You could even generate the actual includes via cmake during the generation of the project which which would shorten the headers to 3 pragmas plus one include; this would have the additional benefit of working with compiler versions not supporting __has_include.
function(my_generate_include OUT_LIST DESTINATION_DIR FIRST_HEADER)
set(GENERATED_HEADERS ${${OUT_LIST}})
foreach(HEADER IN ITEMS ${FIRST_HEADER} ${ARGN})
if (HEADER MATCHES "^old/(.*)$")
configure_file(old_include.hpp.in "${DESTINATION_DIR}/${CMAKE_MATCH_1}")
list(APPEND GENERATED_HEADERS "${DESTINATION_DIR}/${CMAKE_MATCH_1}")
elseif (HEADER MATCHES "^new/(.*)$")
configure_file(new_include.hpp.in "${DESTINATION_DIR}/${CMAKE_MATCH_1}")
list(APPEND GENERATED_HEADERS "${DESTINATION_DIR}/${CMAKE_MATCH_1}")
else()
message(FATAL_ERROR "Header '${HEADER}' doesn't start with 'new/' or 'old/'")
endif()
endforeach()
set(${OUT_LIST} ${GENERATED_HEADERS} PARENT_SCOPE)
endfunction()
...
set(HEADERS)
my_generate_include(HEADERS ${CMAKE_CURRENT_BINARY_DIR}/generated_includes
old/a/b/c.hpp
new/d/e/f.hpp
)
Related
I'm working with a recent version of gcc, writing C++ code (but I would like an answer which would work for C as well). I have various extra warning flags enabled.
In my code, I am including a header from some popular library, #include <mylib.h>. Now, mylib.h triggers some of these compiler warnings; but I need to include it as-is, I can't alter it.
I want to suppress all compiler warnings when including mylib.h. I know I can suppress individual warnings with:
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wwarning-name-here"
#pragma GCC diagnostic ignored "-Wanother-warning-name-here"
// my code goes here
#pragma GCC diagnostic pop
but I want to suppress all of the warnings. Can I do that? Note that I'm willing to tweak the compiler command-line if that helps.
Note: This related question indicates that, in 2015, one could not do this using pragmas.
From GCC manual:
2.7 System Headers
The header files declaring interfaces to the operating system and
runtime libraries often cannot be written in strictly conforming C.
Therefore, GCC gives code found in system headers special treatment.
All warnings, other than those generated by `#warning' (see
Diagnostics), are suppressed while GCC is processing a system header.
Macros defined in a system header are immune to a few warnings
wherever they are expanded. This immunity is granted on an ad-hoc
basis, when we find that a warning generates lots of false positives
because of code in macros defined in system headers.
Normally, only the headers found in specific directories are
considered system headers. These directories are determined when GCC
is compiled. There are, however, two ways to make normal headers into
system headers.
The -isystem command line option adds its argument to the list of
directories to search for headers, just like -I. Any headers found in
that directory will be considered system headers.
All directories named by -isystem are searched after all directories
named by -I, no matter what their order was on the command line. If
the same directory is named by both -I and -isystem, the -I option is
ignored. GCC provides an informative message when this occurs if -v is
used.
There is also a directive, #pragma GCC system_header, which tells GCC
to consider the rest of the current include file a system header, no
matter where it was found. Code that comes before the `#pragma' in the
file will not be affected. #pragma GCC system_header has no effect in
the primary source file.
how to make this happen in CMake?
From CMake manual:
include_directories
Add include directories to the build.
include_directories([AFTER|BEFORE] [SYSTEM] dir1 [dir2 ...])
...
If the SYSTEM option is given, the compiler will be told the directories are meant as system include directories on some platforms. Signalling this setting might achieve effects such as the compiler skipping warnings, or these fixed-install system files not being considered in dependency calculations - see compiler docs.
I'm currently trying to setup a project that will make use of multiple compilers (including Clang, MSVC, and GCC) using Visual Studio 2019's new CMake functionalities (notably using Clang and Ninja in conjunction with CMake and VS2019).
I'm using CMake to configure the project to be "compiler-agnostic", so that I don't need to edit the code itself to handle different compilers via pre-processor instructions or #pragma instructions.
This project needs to be configured to have a high warning level (/W4 for MSVC, -Wall, -Wextra, and -Wpedantic for Clang), and must treat warnings as errors.
I don't have any issues when it comes to configuring the MSVC portion of the project. A lot of these settings have "sane" defaults that "just work" as I'd expect them to. When it comes to Clang, however, I've encountered a problem:
I can't seem to disable warnings for third-party library header files. I'm currently using the Dear Imgui and SFML libraries. Since Dear Imgui isn't pre-compiled, I simply do the following in my CMakeLists.txt file to include it:
include_directories(
${CMAKE_CURRENT_SOURCE_DIR}/libs/imgui
)
I use the statically linked version of SFML, so I do the following to include it:
# Find SFML and link statically to it.
# Note: We need to set the SFML_DIR variable manually.
set(SFML_STATIC_LIBRARIES TRUE)
set(SFML_DIR "${CMAKE_CURRENT_SOURCE_DIR}/libs/SFML-2.5.1/lib/cmake/SFML")
find_package(SFML 2.5.1 COMPONENTS system audio window graphics REQUIRED)
target_link_libraries(${PROJECT_NAME}
PRIVATE
sfml-system
sfml-audio
sfml-window
sfml-graphics
)
Sadly, SFML doesn't currently follow the current CMake standard way of adding libraries, so it's kind of weird to work with or configure it via CMake.
Now, the above works just fine when it comes to including the libraries in my project (but it might be something I need to change, so I've included it in the post). The problems come when I try to impose the warnings and warnings-as-errors configurations to them when using Clang.
Here are the parts of my CMakeLists.txt file which handle Clang and my C++ configurations:
# Set project to use C++ 17 standard.
set_target_properties(
${PROJECT_NAME}
PROPERTIES
CXX_STANDARD 17
CXX_STANDARD_REQUIRED ON
)
target_compile_options(${PROJECT_NAME} PRIVATE
# All warnings, warnings as errors, be pedantic.
-Wall
-Wextra
-Werror
-Wpedantic
# Disable warnings about C++98 incompatibility. We're using C++17 features...
-Wno-c++98-compat
-Wno-c++98-compat-pedantic
)
Using the above configuration results in hundreds of warnings/errors in Dear Imgui's source files (due to the usage of "old-school" C++/C-style code), as well as a whole bunch of them in SFML's own source files and header files.
I've been looking for ways to get around this for nearly a week, before settling on the following solution (which doesn't entirely work, more on that later):
set(LIBS_FOLDER "${CMAKE_CURRENT_SOURCE_DIR}/libs")
set(IMGUI_FOLDER "${LIBS_FOLDER}/imgui")
set(SFML_FOLDER "${LIBS_FOLDER}/SFML-2.5.1/include/SFML")
file(GLOB LIBRARY_FILES
# Dear-imgui
"${IMGUI_FOLDER}/*.cpp"
"${IMGUI_FOLDER}/misc/freetype/*.cpp"
"${IMGUI_FOLDER}/misc/fonts/*.cpp"
"${IMGUI_FOLDER}/misc/cpp/*.cpp"
# SFML
"${SFML_FOLDER}/Audio/*.cpp"
"${SFML_FOLDER}/Graphics/*.cpp"
"${SFML_FOLDER}/Network/*.cpp"
"${SFML_FOLDER}/System/*.cpp"
"${SFML_FOLDER}/Window/*.cpp"
)
set_source_files_properties(
${LIBRARY_FILES}
PROPERTIES
COMPILE_FLAGS
"-Wno-everything"
)
I begin by GLOB-ing my library source files (NOTE: I know GLOB is usually looked down upon, but I felt like using it with third-party library files was fine since they're not supposed to change anyway). I then pass them to the set_source_files_properties function to apply the -Wno-everything flag, which seems to properly suppress all errors and warnings from those files.
Everything seems to work just fine, except for one warning which I can't seem to disable without using a #pragma instruction in my code (which I want to avoid). When compiling an empty main function that includes SFML headers, I get warnings about their .hpp files (which can't be passed to the set_source_files_properties function).
This:
#include <SFML/Graphics.hpp>
int main()
{
}
Results in the following warnings/errors:
zero as null pointer constant [-Werror,-Wzero-as-null-pointer-constant]
declaration is marked with '\deprecated' command but does not have a deprecation attribute [-Werror,-Wdocumentation-deprecated-sync]
declaration is marked with '\deprecated' command but does not have a deprecation attribute [-Werror,-Wdocumentation-deprecated-sync]
declaration is marked with '\deprecated' command but does not have a deprecation attribute [-Werror,-Wdocumentation-deprecated-sync]
In these respective SFML files:
ThreadLocal.hpp (57)
Keyboard.hpp (161)
Event.hpp (105)
PrimitiveType.hpp (52)
Other things I've tried that didn't work:
Putting the .h/.hpp files in the set_source_files_properties CMake function (alongside the .cpp files). Works for Dear Imgui, but all its errors were in .cpp files, not its headers. Doesn't work for SFML's headers.
(Not for SFML, but for Dear Imgui) Including the directories as SYSTEM includes to suppress the warnings. Doesn't appear to work on Windows. Can't really do this with SFML, since I'm using CMake's find_package function instead of doing everything manually.
Using #pragma instructions. While this worked, every SFML file has dozens of different errors, and I want to avoid using #pragmas everywhere (or wrapping SFML's headers in my own headers that just wrap the #include instruction in #pragmas).
Is it even possible to suppress these warnings for my library headers without #pragmas? I've never really used Clang before, so apologies if this seems like a simple question, but searching online hasn't really given me anything that would work:
Outside of a commandline (I'm using Visual Studio with CMake).
On Windows (the system flag doesn't seem to work with this setup).
That would work with CMake specifically.
You can mark the include paths as SYSTEM. Most compilers will not report warnings in system headers. In your case that might look something like this:
include_directories(SYSTEM ${CMAKE_CURRENT_SOURCE_DIR}/libs/imgui)
set_target_properties(sfml-system PROPERTIES INTERFACE_SYSTEM_INCLUDE_DIRECTORIES $<TARGET_PROPERTY:sfml-system,INTERFACE_INCLUDE_DIRECTORIES>)
set_target_properties(sfml-audio PROPERTIES INTERFACE_SYSTEM_INCLUDE_DIRECTORIES $<TARGET_PROPERTY:sfml-audio,INTERFACE_INCLUDE_DIRECTORIES>)
set_target_properties(sfml-window PROPERTIES INTERFACE_SYSTEM_INCLUDE_DIRECTORIES $<TARGET_PROPERTY:sfml-window,INTERFACE_INCLUDE_DIRECTORIES>)
set_target_properties(sfml-graphics PROPERTIES INTERFACE_SYSTEM_INCLUDE_DIRECTORIES $<TARGET_PROPERTY:sfml-graphics,INTERFACE_INCLUDE_DIRECTORIES>)
For example, this dummy project:
project(example)
cmake_minimum_required(VERSION 3.18)
add_library(dep INTERFACE)
target_include_directories(dep INTERFACE include)
file(WRITE include/header.h "static int a;")
add_library(lib STATIC lib.c)
target_link_libraries(lib PRIVATE dep)
target_compile_options(lib PRIVATE -Wunused -Werror)
file(WRITE lib.c "#include <header.h>")
Fails with:
$ cmake . && make
include/header.h:1:12: error: ‘a’ defined but not used [-Werror=unused-variable]
But after adding this line:
set_target_properties(dep PROPERTIES INTERFACE_SYSTEM_INCLUDE_DIRECTORIES $<TARGET_PROPERTY:dep,INTERFACE_INCLUDE_DIRECTORIES>)
It builds with no errors.
There is a way to suppress warnings from third party headers in a CMake way.
target_include_directories(<target> [SYSTEM] [AFTER|BEFORE]
<INTERFACE|PUBLIC|PRIVATE> [items1...]
[<INTERFACE|PUBLIC|PRIVATE> [items2...] ...])
The system keyword is most likely what you want.
If SYSTEM is specified, the compiler will be told
the directories are meant as system include directories on some platforms
The SYSTEM keyword adds -isystem for GCC/Clang. Instead of treating the directory like a normal include directory.
# GCC docs
Warnings from system headers are normally suppressed.
On the assumption that they usually do not indicate real problems
and would only make the compiler output harder to read.
For a while there was no solution for MSVC until recently in CMake 3.22, because the MSVC compiler finally added support for this.
Here is the MSVC blog post where they talk about the new compiler functionality.
The “Ninja” and “NMake Makefiles” generators now use
the MSVC “-external:I” flag for system includes.
This became available as of VS 16.10
(toolchain version 14.29.30037).
We're using clang with -fmodule and -fcxx-module to enable module support as documented at http://clang.llvm.org/docs/Modules.html. We're already seeing a significant improvement in build times by defining module maps for our core libraries.
However, we have some library headers that use pragmas to disable warnings for certain lines, for example:
template <typename TFloat>
static bool exactlyEqual(TFloat lhs, TFloat rhs)
{
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wfloat-equal"
return lhs == rhs;
#pragma clang diagnostic pop
}
When this header is pulled in as a precompiled module, it seems clang's internal representation does not preserve the pragma information and the warning is still emitted. Since we treat warnings as errors this causes compilation to fail. Some might argue to just disable float-equal entirely, but we have a bunch of other cases with different warnings which we don't want to globally disable.
We're already using -Wno-system-headers and -isystem so that clients of libraries generally don't see warnings like this anyway (even without the pragma), but this doesn't seem to work when the header is imported as a module. In addition we still hit these warnings for code internal to the library which includes the header as a non-system header (i.e. without using -isystem / using double quotes), since module precompilation and importing also occurs here.
I've tried using _Pragma(...) instead of #pragma which didn't have any effect.
Is there some other way to conditionally ignore warnings in headers that come from precompiled clang modules?
UPDATE: I've put a sample project up on https://github.com/MikeWeller/ClangModuleWarnings which reproduces the problem
UPDATE: Seems the [system] module attribute will suppress all warnings. However this suppresses even warnings we want to see when building the library itself, and we don't want to make all our modules system modules. If we find a way to not use the library's module map when building the library itself this may be acceptable but we'd still like to pragma out certain warnings for non-system modules..
Suppose I am setting up a fairly strict compiler warning level for my own code but the problem is my code is dependent on an external library which wasn't written too rigorously, so when I include the header file from the external library I get all sorts of warnings. Is there a way I can set different warning levels for different files in CMake?
Here is an example showing the situation, suppose I have main.cpp
#include "external.h"
int main(){
// some code
}
and the corresponding CMakeLists.txt
cmake_minimum_required(VERSION 2.8)
project(Test)
set(PROJECT_SRCS
${PROJECT_SOURCE_DIR}/main.cpp
)
if(MSVC)
if(CMAKE_CXX_FLAGS MATCHES "/W[0-4]")
string(REGEX REPLACE "/W[0-4]" "/W4" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
else()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4")
endif()
elseif(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -W -Wall -Werror -Wshadow")
endif()
include_directories(${EXTERNAL_INC})
add_executable(Test ${PROJECT_SRCS})
target_link_libraries(Test ${EXTERNAL_LIB})
Suppose "external.h" is causing -Wshadow warnings, I can take -Wshadow out but that also means any -Wshadow warnings won't be catched for my native code. I know I can add pragma warning push and pop in main.cpp but this approach will only work for windows. Is it possible to do it in CMake so it works for different platform such as linux and windows and also keep the source code clean?
I know I can add pragma warning push and pop in main.cpp but this
approach will only work for windows.
GCC has diagnostic pragmas, MSVC warning pragmas. Just check for the compiler: MSVC has _MSC_VER, GCC has __GNUC__ predefined.
Is there a way I can set different warning levels for different files
in CMake?
set_source_files_properties allows you to add compile flags to specific source files to override previous options.
You just want to isolate warnings of these (bad written) headers, not of your whole source file, so using pragmas for these includes is the safer way.
No, you can't do anything with CMake regarding this issue. You can only rely on some intrusive compiler-dependant method like push/pop pragma warnings. It seems you know how to do it with MSVC already and here is the link how to do it with GCC.
But why you can't do it with CMake without modifying the headers? Because headers are not standalone files they are get embedded into the cpp ones and only then they are processed. So it is the whole units which should be set-up, not the headers. And in general you don't know what units include what headers. So even if the compiler you use supports per file warning levels it is too brittle to try to use this feature: the header might be included in any other unit later by some different programmer who knows nothing about your strategy — it depends too much on a human which is bad.
It is much easier to create TurnOffWarnings/TurnOnWarnings header files and force the policy in the organization to wrap all the includes with angle brackets between these custom headers.
I have a project that uses log4cxx, boost, etc. libraries whose headers generate lots of (repetitive) warnings. Is there a way to suppress warnings from library includes (i.e. #include <some-header.h>) or includes from certain paths? I'd like to use -Wall and/or -Wextra as usual on project code without relevant info being obscured. I currently use grep on make output but I'd like something better.
You may try to include library headers using -isystem instead of -I. This will make them "system headers" and GCC won't report warnings for them.
For those using CMake, you can modify your include_directories directives to include the symbol SYSTEM which suppresses warnings against such headers.
include_directories(SYSTEM "${LIB_DIR}/Include")
^^^^^^
You can use pragmas. For example:
// save diagnostic state
#pragma GCC diagnostic push
// turn off the specific warning. Can also use "-Wall"
#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
#include <boost/uuid/uuid.hpp>
#include <boost/uuid/uuid_generators.hpp>
#include <boost/uuid/uuid_io.hpp>
#include <boost/lexical_cast.hpp>
// turn the warnings back on
#pragma GCC diagnostic pop
I found the trick. For library includes, instead of -Idir use -isystem dir in the makefile. GCC then treats boost etc. as system includes and ignores any warnings from them.
#pragma are instructions to the compiler. you can set something before the #include and disable it after.
You can also do it at the command line.
Another GCC page specifically on disabling warnings.
I would go for the option of using #pragma's within the source code, and then providing a
sound reason (as a comment) of why you are disabling the warnings. This would mean reasoning about the headers files.
GCC approaches this by classifying the warning types. You can classify them to be warnings or to be ignored. The previously linked articles will show you which warnings are may be disabled.
Note: you can also massage the source code to prevent certain warnings by using attributes; however, this bind you quite closely to GCC.
Note2: GCC also uses the pop/push interface as used in microsoft's compiler -- Microsoft disables warnings through this interface. I suggest you investigate this further , as I do not know if it is even possible.
Putting the following
#pragma GCC system_header
will turn off GCC warnings for all following code in this file.
You can try using precompiled headers. Warnings won't go away but at least the won't show up in your main compilation.
If you need to explicitly override a system header then you're restricted to pragmas. You can verify which includes you're using via make depend output.
Also see diagnostic push-pop for gcc >= 4.6
Another way to do it is, in the makefile, to tell the compiler to ignore warnings for the specific folder:
$(BUILD_DIR)/libs/%.c.o: CFLAGS += -w
There must be reasons for those warnings. These will either be caused by errors in your code that uses the library, or by errors in the library code itself. In the first case, fix your code. In the second case, either stop using the library or if it is FOSS code, fix it.