Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Investigate and address issue with change to -isystem includes for IMPORTED targets (#299) #443

Closed
7 tasks done
bartlettroscoe opened this issue Feb 1, 2022 · 34 comments
Closed
7 tasks done

Comments

@bartlettroscoe
Copy link
Member

bartlettroscoe commented Feb 1, 2022

CC: @mperrinel, @marcinwrobel1986, @keitat

Parent Issues:

Other Linked issues:

Description of the problem

While studying a build errors reported with SPARC on the Vanguard system van1-tx2 on the machine 'stria' using the ARM C++ 20.1 compiler (see tribitsrefactoring#3 and SPAR-1074) for the updated TriBITS implementation increment to modern CMake targets in trilinos/Trilinos#9978 (which is reapplying and fixing trilinos/Trilinos#9894 after it was reverted), I traced the problem to a change in how include directories and specified when changing to modern cmake IMPORTED targets.

With old TriBITS, a downstream customer CMake project would get the Trilinos include directories by either setting the directory-level INCLUDE_DIRECTORIES property with:

include_directories( [SYSTEM] ${Trilinos_INCLUDE_DIRS})

or at the target level with:

target_include_directories(downstream PUBLIC|PRIVATE [SYSTEM] ${Trilinos_INCLUDE_DIRS})

and the project could either include these with SYSTEM or not and have them included on the compile line with -isystem or just -I, respectively. SPARC and many other application codes usually omit SYSTEM so the Trilinos include dirs are specified as -I on the compile lines of the downstream customer object builds.

But with this change to modern CMake IMPORTED targets as per trilinos/Trilinos#9894 (and then again after the revert of that with trilinos/Trilinos#9978) to have the INTERFACE_INCLUDE_DIRECTORIES property set, by default, downstream targets add those include directories as SYSTEM includes and apply them as -isystem on the compile lines. This changes the search order for header files and also silences warnings coming from those header files as noted in "Professional CMake: 10th edition" which states:

It is also worth noting that paths specified by an imported target’s INTERFACE_INCLUDE_DIRECTORIES
property will be treated by consuming targets as though they were SYSTEM paths by default.
This is because imported targets are assumed to be coming from outside the project and therefore their
associated headers should be treated in a similar way to other system-provided headers. The
project can override this behavior by setting the consuming target’s NO_SYSTEM_FROM_IMPORTED
property to true, which will prevent all of the imported targets it consumes from being treated as
SYSTEM. Imported targets are covered in detail in Chapter 17, Target Types.

In the case of this SPARC build on 'stria', this change of the Trilinos install include dir from -I to -isystem results in gtest/gtest.h being found in the INCLUDE env path var under:

/opt/atse/libs/arm/yaml-cpp/0.6.2/include/

(because yaml-cpp installs with gtest by default, see jbeder/yaml-cpp#539) instead of under the Trilinos install dir:

<tril-install-prefix>/install/

(because Trilinos installs gtest for some customers, see trilinos/Trilinos#8001).

Yes, this a crazy system where the compiler (arm-20.1 C++ in this case) searches for headers in directories listed in an env var INCLUDE before it searches directories listed in explicit -isystem arguments but that is what it does. And yes, it is terrible that libraries like Trilinos and yaml-cpp are installing gtest, but that is what we have.

This is a fragile environment where header files of the same name are found under multiple include directories and the search order of the include directories is critical. But this is often what happens on real systems and a change in how include directories are handled can be very difficult to deal with unless you are given precise control of how include directories are handled and passed to the compiler.

Possible workarounds that can be implemented in TriBITS

TriBITS Workaround #1 All CMake versions: Set cache var CMAKE_NO_SYSTEM_FROM_IMPORTED=ON by default in TrilinosConfig.cmake:

Details (click to expand)

.

One workaround that confirmed worked (see here) is to set CMAKE_NO_SYSTEM_FROM_IMPORTED=TRUE by adding the statement:

set(CMAKE_NO_SYSTEM_FROM_IMPORTED ON CACHE BOOL "")

to the installed file <Project>Config.cmake. That resulted in all of the IMPORTED targets include directories to change from -isystem to -I and it fixed the particular SPARC build issue. But it also changed from -isystem to -I for the includes of some non-Trilinos external packages and therefore has the potential of exposing the project to new build warnings and changing what header files are found for these packages.

Rather than hard-code setting CMAKE_NO_SYSTEM_FROM_IMPORTED=ON, a TriBITS project could provide a configure-time option called something like:

  -D <Project>_SET_CMAKE_NO_SYSTEM_FROM_IMPORTED=TRUE

and if TRUE, would add the statement set(CMAKE_NO_SYSTEM_FROM_IMPORTED ON CACHE BOOL "") to all of the installed <Project>Config.cmake and <Package>Config.cmake files.'

But this does not maintain backwards compatibility because it also changes all of the IMPORTED targets (including non-TriBITS packages) include directories from -isystem to -I which can break the build.

  • Pro: Put back Trilinos include dirs as -I
  • Pro: Works with all versions of CMake
  • Con: Changes other IMPORTED target includes from -isystem to -I which can break the build

TriBITS Workaround #2: For CMake versions >= 3.23: Set IMPORTED_NO_SYSTEM on all generated IMPORTED targets

Details (click to expand)

.

But for CMake 3.23+, we would have a TriBITS project-level cache var option <Project>_IMPORTED_NO_SYSTEM that would be set to TRUE by default that would set the IMPORTED target property IMPORTED_NO_SYSTEM on all of the generated TriBITS package targets. And with <Project>_SET_CMAKE_NO_SYSTEM_FROM_IMPORTED=FALSE by default, that would give about perfect backwards compatibility.

  • Pro: Maintains Trilinos includes as -I without changing include dirs for other IMPORTED targets (i.e. maintains backward compatibiilty)
  • Con: Requires CMake 3.23

Possible workarounds that can be implemented in the customer CMake projects

Customer Workaround #1: All CMake versions: Customer CMake projects set CMAKE_NO_SYSTEM_FROM_IMPORTED=TRUE .

Details (click to expand>

.

The downstream customer CMake project can set the cache var CMAKE_NO_SYSTEM_FROM_IMPORTED=TRUE or set the target property NO_SYSTEM_FROM_IMPORTED=TRUE at a more fine-grained level. But this will also have the blunt-force effect of changing all of the IMPORTED targets include dirs from -isystem to -I which breaks backwards compatibility for how Trilinos used to work with old TriBITS.

The downstream customer could also address the header search problem directly be removing undesirable search directories, undesirable header files, etc. But that is not always possible (at least not without help from the sysadmins and other people maintaining these systems and software stacks).

But even if the header file search issue is addressed, changing from -I to -isystem for included TriBITS packages will silence warnings that may otherwise have been desirable for the consuming project to see. For example, the Kokkos header files will generate useful warnings if Kokkos is used incorrectly and we don't want to silence these warnings by including the Kokkos headers with -isystem.

  • Pro: Put back Trilinos include dirs as -I
  • Pro: Works with all versions of CMake
  • Con: Changes other IMPORTED target includes from -isystem to -I which can break the build

Customer Workaround #2: All CMake versions: Directly address the duplicate header file finds:

Examples:

  • Remove duplicate headers
  • Remove include dirs that have duplicate headers
  • Other application-specific fixes (e.g. have SPARC bring in its own private GTest)

Pros and cons:

  • Pro: Works with all CMake versions
  • Pro: Makes for a more robust development enviornment
  • Con: Requires addressing on a system-by-system, and build-by-build basis

Desired features in CMake to address the problem

The best approach would be for CMake to provide more control for how include directories are handled on a per-target basis both from the IMPORTED target itself and for consumers of IMPORTED targets to have fine-grained control for how include directories for specific upstream targets are handled.

Details (click to expand)

.

@bradking noted below that CMake 3.23 will introduce the IMPORTED_NO_SYSTEM target property that, if set to TRUE, then the INTERFACE_INCLUDE_DIRECTORIES property from that IMPORTED target would be specified as -I but the other IMPORTED targets include directories would stay as -isystem. That would allow TriBITS to set the property IMPORTED_NO_SYSTEM=TRUE by default on all generated targets and therefore maintain perfect backwards compatibility for downstream customers CMake projects. (But this should be a TriBITS configure-time option to set that or not.)

Also, it would be good if CMake would allow downstream CMake projects to set the SYSTEM or no-SYSTEM handling for linked-in IMPORTED targets include directories on an upstream target-by-target basis like:

target_link_libraries( <consuming-target>
   PUBLIC NO_SYSTEM <target1> <target2>
   PRIVATE SYSTEM <target3> <target4>
   )

And with that, the INTERFACE_INCLUDE_DIRECTORIES from <target1> and <target2> would be listed on compile line with -I and those with <target3> and <target4> would be listed on compile line with -isystem.

That would return full control of how header files are handled in the downstream CMake project that is lost with the move to modern CMake.

NOTE: Until CMake supports this (or if it never supports this), one can change the IMPORTED_NO_SYSTEM property on an IMPORTED library target after it has been formed in the downstream CMake project in the find_package() but before it gets used in a target_link_libraries() command. The same is true for the IMPORTED_SYSTEM_INCLUDE_DIRECTORIES list (but in that case, one must be careful to keep that in sync with the IMPORTED_INCLUDE_DIRECTORIES property). With those manual IMPORTED target property tweaks, the downstream CMake project should be able to set or unset the SYSTEM properties of any of the include directories of upstream IMPORTED libraries used in the project. (We would need to do some detailed experiments to see if this is correct and what the limits to this may be. See below)

Tasks

@bartlettroscoe bartlettroscoe created this issue from a note in TriBITS Refactor (Selected) Feb 1, 2022
@bartlettroscoe bartlettroscoe changed the title Investigate and address issue with change to -isystem includes for IMPORTED targets Investigate and address issue with change to -isystem includes for IMPORTED targets (#299) Feb 2, 2022
@bartlettroscoe
Copy link
Member Author

To summarize how the SPARC build error occurred, 5 things all had to occur on this system as outlined in tribitsrefactoring#3 which are:

  • 1. Changing over to modern CMake targets to bring in include dirs for Trilinos results in CMake assigning SYSTEM to the include directories pulled in through the IMPORTRED targets.

    • Workaround: Downstream CMake project can set NO_SYSTEM_FROM_IMPORTED=TRUE on consuming targets or set project-level variable CMAKE_NO_SYSTEM_FROM_IMPORTED=TRUE. Or just set CMAKE_NO_SYSTEM_FROM_IMPORTED=TRUE in the <Project>Config.cmake file but then that affects all targets in downstream project or all include directories on IMPORTED targets on the specific consuming target and may changes some existing non-Trilinos IMPORTED package includes from -isystem to -I with the associated consequences. Below comment will investigate this further.
  • 2. When an include dir is set both as a directory property using include_directories(<some-include-dir>) and pulled in from the INTERFACE_INCLUDE_DIRECTRORIES target property from an IMPORTED target, the imported target property is used (in this case SYSTEM). (Therefore, calling include_directories(<some-include-dir>) without SYSTEM is essentially ignored.)

    • No known workaround (would require a CMake extension.)
  • 3. The install of /opt/atse/libs/arm/yaml-cpp/0.6.2/include also includes gtest/gtest.h as well (see Do not install Google {Mock,Test} jbeder/yaml-cpp#539 and https://bugs.archlinux.org/task/57361). Therefore, if the module yaml-cpp/0.6.2 is loaded, then /opt/atse/libs/arm/yaml-cpp/0.6.2 is appended to the INCLUDE env var and gtest/gtest.h is found by default by the compiler.

    • Workaround: Delete the directory /opt/atse/libs/arm/yaml-cpp/0.6.2/include/gtest since yaml-cpp lib does not use it and use -D YAML_CPP_BUILD_TOOLS=OFF from now on when installing yaml-cpp.
  • 4. With the arm-20.1 compiler on 'stria', the include directories specified in the env var INCLUDE are searched before those passed in through explicit -isystem arguments to the compiler! (And therefore, gtest/gtest.h is found /opt/atse/libs/arm/yaml-cpp/0.6.2/include before in the Trilinos install include dir.)

    • No known workaround.
  • 5. SPARC is using GTest installed from Trilinos which is now being pulled in with -isystem and should instead be using its own GTest (like EMPIRE and several Trilinos packages do). If SPARC was using its own GTest, then it would be its own unique include dir passed in with -I and it would be found first. (see SPAR-614 and Trilinos #8001.)

    • BEST SOLUTION (not a workaround): have SPARC use its own version of gtest and its own unique -I include path.

All 5 of these things had to happen in order for this error to occur. If you address/change any one of these 5 things (with the suggested fixes or workarounds), then the this error goes away and SPARC builds and runs fine.

Basically, this is just a fragile system setup and is begging for trouble. We have created the problem for ourselves by:

@bartlettroscoe
Copy link
Member Author

FYI: I have asked Kitware for their advice on how to deal with this in:

@bradking
Copy link
Contributor

bradking commented Feb 2, 2022

CMake 3.23 introduces the IMPORTED_NO_SYSTEM target property that can be set on an imported target to prevent its include directories from being treated as system includes.

@bartlettroscoe
Copy link
Member Author

CMake 3.23 introduces the IMPORTED_NO_SYSTEM target property that can be set on an imported target to prevent its include directories from being treated as system includes.

@bradking, excellent! That matches my suggested property IMPORTED_INCLUDE_DIRECTORIES_AS_NO_SYSTEM above. I will build a 'master' version of CMake and test that out with this SPARC build and see if this behaves as we need it to to maintain backward compatibility. (And I will update the above comment to acknowledge IMPORTED_NO_SYSTEM coming in CMake 2.23.)

So with CMake 3.22.0 being released on 2021-11-18, should we expect CMake 3.23.0 to be released at end of March 2022 (i.e. a new CMake release every 4 months)?

But even if IMPORTED_NO_SYSTEM does work as it should, that would require every Trilinos customer to upgrade to CMake 3.23 in order to maintain backward compatibility. To ensure that, we would have to raise the minimum required version of CMake for TriBITS and Trilinos to CMake 3.23 to maintain backward compatibility with the expression of include directories in downstream CMake projects. And CMake 3.23.0 is not even released yet and with people being nervous with .0 releases, we would likely have to wait for CMake 3.23.1. So I think we are looking at the end May 2022 until CMake 3.23.1 is released? And since it takes about a year before a release of CMake becomes ubiquitous on all the HPC machines, that may mean that we can't require CMake 3.23 until May 2023. That means that all development on TriBITS would need to stop or we would need to develop TriBITS in isolation for a year and wait to release it to customers until we can require CMake 3.23 is the minimum version.

So waiting for and requiring CMake 3.23 does not seem like a reasonable uniform solution. (But it could be a good solution for some customers that have very challenging fragile environments so we should still support IMPORTED_NO_SYSTEM with TriBITS with CMake 3.23+ so they can take advantage of this.)

So without CMake 3.23, as a non-ideal workaround, what do you think of having TriBITS, by default, add:

set(CMAKE_NO_SYSTEM_FROM_IMPORTED ON CACHE BOOL "")

to the <Project>Config.cmake and <Package>Config.cmake files? That would keep the Trilinos include as -I. That is non-ideal but it will work with older versions of CMake and if the only IMPORTED targets that a downstream CMake project uses are Trilinos libraries, then that would maintain backward compatibility. Even in this SPARC case where setting CMAKE_NO_SYSTEM_FROM_IMPORTED=ON changed the include dirs for two IMPORTED packages from -isystem to -I the results seems to be benign.

What do you think? I need some advice here.

@bartlettroscoe
Copy link
Member Author

Just to give some more detail on what is happening with the SPARC build on 'stria' with that fragile env and the gtest problem, as described in the internal issue tribitsrefactoring#3 ...

The old TriBITS for Trilinos results in the SPARC compile include dirs using -I for the Trilinos include and -isystem for the euclid-20.23 and sgm-20.23 packages in the order:

...
-I/projects/sparc/tools/python/3.7.9/aarch64/lib/python3.7/site-packages/numpy/core/include
-I/lustre/rabartl/SPARC.base/sparc/Trilinos/van1-tx2_arm-20.1_openmpi-4.0.5_openmp_static_opt_atdm/include
-I/opt/atse/libs/arm/openmpi4/netcdf/4.6.3/include
...
-Isrc
...
-I/lustre/rabartl/SPARC.base/sparc/sparc-itar/include-OUO-ITAR
-isystem projects/sparc/tpls/van1-tx2/euclid-20.23/49b290856f56828303cd3e7a427721b4d2cc571c/van1-tx2_arm-20.1_openmpi-4.0.5/include
-isystem projects/sparc/tpls/van1-tx2/sgm-20.23/642d571ff38721f82cb08c2d186a9c283b92da38/van1-tx2_arm-20.1_openmpi-4.0.5/include

Note that the above means that:

  • The headers under the Trilinos include dir will be found before any of the system include directories (such as those listed with -isystem and from the INCLUDE env var) and warnings emitted from these headers will be shown when compiling the downstream object files.
  • The headers for the external packages euclid-20.23 and sgm-20.23 will be found late as -isystem headers (even after those listed in the INCLUDE env var) and they will not emit any compile warnings.

The new TriBITS without setting CMAKE_NO_SYSTEM_FROM_IMPORTED gives the include dirs:

...
-I/lustre/rabartl/SPARC.base/sparc/sparc-itar/include-OUO-ITAR
-isystem /lustre/rabartl/SPARC.base/sparc/Trilinos/van1-tx2_arm-20.1_openmpi-4.0.5_openmp_static_opt_atdm/include
-isystem /projects/sparc/tpls/van1-tx2/euclid-20.23/49b290856f56828303cd3e7a427721b4d2cc571c/van1-tx2_arm-20.1_openmpi-4.0.5/include
-isystem /projects/sparc/tpls/van1-tx2/sgm-20.23/642d571ff38721f82cb08c2d186a9c283b92da38/van1-tx2_arm-20.1_openmpi-4.0.5/include

The big change here is that the Trilinos include dir is now listed at the end of the compile line with -isystem. This is what breaks the build since the headers in the env var INCLUDE and selected first which picked the wrong gtest/gtest.h file under the yaml-cpp/0.6.3 install dir. Another issue is that now any warnings coming from Trilinos header files will be suppressed. This can be bad because Kokkos, for example, can emit some very useful warnings when not used correctly and you don't want to suppress those compiler warnings.

And finally, the new TriBITS with setting CMAKE_NO_SYSTEM_FROM_IMPORTED=ON gives the include dirs:

...
-I/projects/sparc/tools/python/3.7.9/aarch64/lib/python3.7/site-packages/numpy/core/include
-I/lustre/rabartl/SPARC.base/sparc/Trilinos/van1-tx2_arm-20.1_openmpi-4.0.5_openmp_static_opt_atdm/include
-I/opt/atse/libs/arm/openmpi4/netcdf/4.6.3/include
...
-Isrc
...
-I/lustre/rabartl/SPARC.base/sparc/sparc-itar/include-OUO-ITAR
-I/projects/sparc/tpls/van1-tx2/euclid-20.23/49b290856f56828303cd3e7a427721b4d2cc571c/van1-tx2_arm-20.1_openmpi-4.0.5/include
-I/projects/sparc/tpls/van1-tx2/sgm-20.23/642d571ff38721f82cb08c2d186a9c283b92da38/van1-tx2_arm-20.1_openmpi-4.0.5/include

This restores the -I include of the Trilinos include dir and puts it back in the same order where it was with the old TriBITS but now the includes for the external packages euclid-20.23 and sgm-20.23 have changed from -isystem to -I. That technically could change what headers are found (but does not in this case because these are rare TPLs) but it could result in warnings being emitted from the header files for euclid-20.23 and sgm-20.23 that would not be emitted if they were passed in with -isystem. If -Werror were used with the build, this could break the build. (Again, that did not happen with this SPARC build but it could happen with other customer CMake projects with a similar situation.)

NOTE: I also experimented that setting CMAKE_NO_SYSTEM_FROM_IMPORTED=TRUE in the TrilinosConfig.cmake file had the same effect as setting it in the outer SPARC CMake project so we could implement that as a workaround for TriBITS to make this transition a little smoother (but not perfect because it may cause different header files to be found and to emit warnings).

@bartlettroscoe bartlettroscoe moved this from Selected to In Progress in TriBITS Refactor Feb 2, 2022
@bartlettroscoe
Copy link
Member Author

@bradking, While we are discussing CMake features, what about allowing the downstream CMake projects more fine-grained control on how they treat include directories for upstream IMPORTED targets like suggested above? Yes, the new CMake 3.23 property IMPORTED_NO_SYSTEM allows the upstream package to influence how its include directories are used in downstream CMake project targets but it does not seem to allow the downstream CMake project targets to override that if desired/needed. When using lists of include directories with old-school CMake, downstream CMake projects had precise control of how include directories were listed on the compile line by calling include_directories([SYSTEM] <include-dir>) or target_include_directories(<downstream-target> PUBLIC|PRIVATE [SYSTEM] <include-dir>). It seems we have lost that with modern CMake (and the consequences can be disastrous on some fragile systems).

@bartlettroscoe
Copy link
Member Author

@bradking, is it possible for a downstream CMake project to edit the properties on an IMPORTED target after they are defined? For example, could a downstream CMake project change the value of the IMPORTED_NO_SYSTEM property on an IMPORTED target after it was defined from calling find_package(...)? If so, that would allow a downstream customer CMake project to control how the include dirs for a given upstream target are listed on the compile line for its downstsream object builds.

I could experiment with this to see but I though it would be worth asking if this is a supported CMake behavior before I try.

@bradking
Copy link
Contributor

bradking commented Feb 2, 2022

by default, add: set(CMAKE_NO_SYSTEM_FROM_IMPORTED ON CACHE BOOL "") to the [package config files]

I think that would be okay so long as it is guarded by some option the project can set to avoid it.
Also, you should have some transition plan for removing the setting later.

more fine-grained control on how they treat include directories for upstream IMPORTED targets

For reference, that suggestion is:

target_link_libraries( <consuming-target>
   PUBLIC NO_SYSTEM <target1> <target2>
   PRIVATE SYSTEM <target3> <target4>
   )
It might be possible but will require deep design work (click to expand details):

The target_link_libraries command internally just maps to storing the named targets in LINK_LIBRARIES and INTERFACE_LINK_LIBRARIES target properties. We'd need a way to represent the "systemness" of each entry in those properties. Also, currently the notion of an include directory being "system" is a usage requirement carried by INTERFACE_SYSTEM_INCLUDE_DIRECTORIES. If any linked target's usage requirements list a directory in that property, it will be considered a system include directory regardless of what anything else says. Achieve the proposed semantics may require a redesign on how include directories and their "system" marks are tracked.

is it possible for a downstream CMake project to edit the properties on an IMPORTED target after they are defined?

Yes. This might be the best approach if the motivating problem only affects a few projects.

@bartlettroscoe
Copy link
Member Author

@bradking

by default, add: set(CMAKE_NO_SYSTEM_FROM_IMPORTED ON CACHE BOOL "") to the [package config files]

I think that would be okay so long as it is guarded by some option the project can set to avoid it.
Also, you should have some transition plan for removing the setting later.

I think what we would do is to have a TriBITS project-level cache var option <Project>_SET_CMAKE_NO_SYSTEM_FROM_IMPORTED that would be set to TRUE by default for CMake versions < 3.23 and FALSE for CMake versions >= 3.23. If <Project>_SET_CMAKE_NO_SYSTEM_FROM_IMPORTED=TRUE, then TriBITS would put in set(CMAKE_NO_SYSTEM_FROM_IMPORTED ON CACHE BOOL "") in all of the installed package config files. That way, a downstream project could still override this by setting the cache var CMAKE_NO_SYSTEM_FROM_IMPORTED=FASLE but otherwise would be getting Trilinos include dirs with -I (and other IMPORTED targets would change from -isystem to -I but those are the breaks).

But for CMake 3.23+, we would instead have a TriBITS project-level cache var option <Project>_IMPORTED_NO_SYSTEM that would be set to TRUE by default that would set the IMPORTED target property IMPORTED_NO_SYSTEM on all of the generated TriBITS package targets. And with <Project>_SET_CMAKE_NO_SYSTEM_FROM_IMPORTED=FALSE by default, that would give about perfect backwards compatibility.

That way, if a customer grabbed an updated version of TriBITS or Trilinos with CMake 3.23+, they would get the about exact same handling of include dirs as with the old TriBITS. But if they used an updated TriBITS or Trilinos with CMake < 3.23, then they would still be including Trilinos headers with -I but would be getting all of their other IMPORTED libraries headers changes from -isystem to -I (which could break fragile builds and bring in more compiler warnings).

And we would have detailed documentation for TriBITS and Trilinos (and release notes) that explained all of this and described the options <Project>_SET_CMAKE_NO_SYSTEM_FROM_IMPORTED and <Project>_IMPORTED_NO_SYSTEM (for CMake 3.23+) and what impact these have and give advice on how customer codes and manipulate these to address problems that may occur with this upgrade and to control their include dir handling as they desire.

What do you think about that?

is it possible for a downstream CMake project to edit the properties on an IMPORTED target after they are defined?

Yes. This might be the best approach if the motivating problem only affects a few projects.

Excellent! Is that documented anywhere? I am assuming you need to adjust these IMPORTED properties before using the IMPORTED target in a target_link_libraries() call?

Also, for this to be useful, I think we need CMake 3.23+ so we can manipulate the IMPORTED_NO_SYSTEM property (and even set it on IMPORTED targets where it has not been set in find_packge()).

It might be possible but will require deep design work

It might be nice to start to carefully document the current behavior of all of these target properties and how they impact how the include directories are treated by downstream targets. For example, you mix and match the properties SYSTEM and IMPORTED_NO_SYSTEM with stacks of dependent libraries, how is that manifested in downstream targets that pull in the IMPORTED targets created from these? Again, I can create so elaborate test cases to determine the CMake behavior as of CMake 3.23 but it would be good to have some automated tests and documentation that pin this down.

@bartlettroscoe
Copy link
Member Author

NOTE: I just got confirmation from the SPARC developers that they want to keep pulling in the Trilinos include dirs with -I since they use pragmas to silence specific warnings from specific Trilinos header files but they want to keep getting warnings from some Trilinos headers like Kokkos when they help catch mistakes in their code.

This is just more motivation to set CMAKE_NO_SYSTEM_FROM_IMPORTED=ON by default in the installed Trilinos package config files.

@bradking
Copy link
Contributor

bradking commented Feb 2, 2022

What do you think about [<Project>_SET_CMAKE_NO_SYSTEM_FROM_IMPORTED and <Project>_IMPORTED_NO_SYSTEM]?

Having those options with defaults based on the version of CMake sounds fine to me.

Is [downstream editing of IMPORTED target properties] documented anywhere?

AFAIK there is no documentation explicitly encouraging or discouraging that. There is no mechanism to enforce it either way.

document the current behavior...you mix and match the properties

There is documentation at the bottom of this section in the cmake-buildsystem(7) manual. It describes how imported targets' INTERFACE_INCLUDE_DIRECTORIES are treated as if they were also in INTERFACE_SYSTEM_INCLUDE_DIRECTORIES. That's the behavior that NO_SYSTEM_FROM_IMPORTED and IMPORTED_NO_SYSTEM control (I need to update that page to mention the latter). If any targets actually populate INTERFACE_SYSTEM_INCLUDE_DIRECTORIES, that will be used unconditionally.

@bartlettroscoe
Copy link
Member Author

Having those options with defaults based on the version of CMake sounds fine to me.

Thanks @bradking, I think we have a workable plan above then. Thanks for your feedback and perspective.

document the current behavior...you mix and match the properties

There are several things about include directories that seems to be unspecified. For example, how are the include directories listed in an IMPORTED target's INTERFACE_SYSTEM_INCLUDE_DIRECTORIES property treated when the IMPORTED target also has the property IMPORTED_NO_SYSTEM set to TRUE? I would assume that these include directories will still be treated as SYSTEM includes in the downstream target (and listed with -isystem on the compile line) but is that the case? I can't find any documentation that says anything about that (and the book "Professional CMake: 10th edition" never even mentions INTERFACE_SYSTEM_INCLUDE_DIRECTORIES).

What I did not realize until reading the CMake documentation for INTERFACE_SYSTEM_INCLUDE_DIRECTORIES is that it is just a listing of include directories already listed INTERFACE_INCLUDE_DIRECTORIES. So INTERFACE_SYSTEM_INCLUDE_DIRECTORIES is just a a data-structure for marking which directories in INTERFACE_INCLUDE_DIRECTORIES that should be treated as SYSTEM directories.

Also, what the CMake documentation fails to mention is that marking a directory as SYSTEM will also change where it appears on the compiler commandline and in the compiler search order (including w.r.t. env vars that may also list include directories). It has more impact that just silencing compiler warnings.

@bradking
Copy link
Contributor

bradking commented Feb 3, 2022

There are several things about include directories that seems to be unspecified

I opened CMake MR 6942 to update the documentation for those.

INTERFACE_SYSTEM_INCLUDE_DIRECTORIES property treated when the IMPORTED target also has the property IMPORTED_NO_SYSTEM

It's not affected. I included that in the documentation update.

marking a directory as SYSTEM will also change where it appears

I've also mentioned the ordering change in the documentation update.

FYI, compilers always search -isystem directories after -I directories regardless of the order of those flags on the command line. CMake moves them to the end of the command line to be more explicit, and so that the re-ordering is also applied on compilers that don't have -isystem flags.

@bartlettroscoe
Copy link
Member Author

and so that the re-ordering is also applied on compilers that don't have -isystem flags.

@bradking, are there compilers that don't support silencing warnings from system headers?

@bradking
Copy link
Contributor

bradking commented Feb 3, 2022

There are compilers that only have -I or equivalent and do not have -isystem or equivalent. One of the major ones was MSVC, which only in the last couple of years started to have versions that support an equivalent to -isystem. For such compilers, we still re-order the includes for consistency, but they are still passed with -I or equivalent.

@bartlettroscoe
Copy link
Member Author

NOTE: If any an include directory from any target is listed in the property INTERFACE_SYSTEM_INCLUDE_DIRECTORIES it will be listed as -isystem. So to make an include directory not a system directory after the targets are created, you would have to search all upstream targets and remove the include dirs from all of the INTERFACE_SYSTEM_INCLUDE_DIRECTORIES properties.

@bartlettroscoe
Copy link
Member Author

bartlettroscoe commented Feb 3, 2022

After talking with @bradking and others at the TriBITS Refactoring meeting, I think we have come to the following decision:

  • Don't change the default behavior of IMPORTED modern CMake targets in downstream projects (i.e. Trilinos include dirs are -isystem by default)
  • Implement TriBITS project-level cache var option <Project>_IMPORTED_NO_SYSTEM that would be set to FASLE by default that if set to TRUE would set the IMPORTED target property IMPORTED_NO_SYSTEM to TRUE. (For versions of CMake < 3.23, issue a configure error.)
  • Provide detailed documentation in TriBITS and release notes in TriBITS and Trilinos about the change from -I to -isystem when pulling in modern CMake targets and how to adjust prior to CMake 3.23 and after CMake 3.23.

bartlettroscoe added a commit to bartlettroscoe/TriBITS that referenced this issue Feb 4, 2022
)

I need this to test out the IMPORTED_NO_SYSTEM property that will be part of
CMake 3.23.0 but is just on 'master' for now.
bartlettroscoe added a commit to bartlettroscoe/TriBITS that referenced this issue Mar 4, 2022
…Pub#443)

* Mentioned dependence on CMake 3.23+

* Mentioned the behavior differences between -I and -isystem

* Mentioned it restores backward compatibility for the move to modern CMake

* Fixed typos and misspellings
bartlettroscoe added a commit to bartlettroscoe/TriBITS that referenced this issue Mar 4, 2022
…Pub#443)

* Mentioned dependence on CMake 3.23+

* Mentioned the behavior differences between -I and -isystem

* Mentioned it restores backward compatibility for the move to modern CMake

* Fixed typos and misspellings
bartlettroscoe added a commit to bartlettroscoe/TriBITS that referenced this issue Mar 4, 2022
…Pub#443)

* Mentioned dependence on CMake 3.23+

* Mentioned the behavior differences between -I and -isystem

* Mentioned it restores backward compatibility for the move to modern CMake

* Fixed typos and misspellings
bartlettroscoe added a commit to bartlettroscoe/TriBITS that referenced this issue Mar 4, 2022
…Pub#443)

* Mentioned dependence on CMake 3.23+

* Mentioned the behavior differences between -I and -isystem

* Mentioned it restores backward compatibility for the move to modern CMake

* Fixed typos and misspellings
bartlettroscoe added a commit to bartlettroscoe/TriBITS that referenced this issue Mar 4, 2022
…Pub#443)

* Mentioned dependence on CMake 3.23+

* Mentioned the behavior differences between -I and -isystem

* Mentioned it restores backward compatibility for the move to modern CMake

* Fixed typos and misspellings
bartlettroscoe added a commit to bartlettroscoe/TriBITS that referenced this issue Mar 4, 2022
…Pub#443)

* Mentioned dependence on CMake 3.23+

* Mentioned the behavior differences between -I and -isystem

* Mentioned it restores backward compatibility for the move to modern CMake

* Fixed typos and misspellings
bartlettroscoe added a commit to bartlettroscoe/TriBITS that referenced this issue Mar 4, 2022
…Pub#443)

* Mentioned dependence on CMake 3.23+

* Mentioned the behavior differences between -I and -isystem

* Mentioned it restores backward compatibility for the move to modern CMake

* Fixed typos and misspellings
bartlettroscoe added a commit to bartlettroscoe/Trilinos that referenced this issue Mar 4, 2022
…ty with -isystem include dirs

Hopefully this will be enough for Trilinos users to work through problems with
changes in the handling of include directories.

For more details, see TriBITSPub/TriBITS#443
@bartlettroscoe
Copy link
Member Author

@mperrinel, can you please do a detailed review of the following PRs as connected set:

?

Once you are finished that, can you please review (with a formal GitHub review with comments) the PRs listed under the "In Review" column in order shown in:

?

@mperrinel
Copy link
Collaborator

@bartlettroscoe
I reviewed the PR #459 and it is good. I put a comment about a potential missing info in the release note. The way you describe the behavior of the new CACHE variable sounds good for me.

@mperrinel
Copy link
Collaborator

mperrinel commented Mar 4, 2022

@bartlettroscoe
I reviewed the PR #456 and it seems good for me. I wrote some comments.

bartlettroscoe added a commit to bartlettroscoe/Trilinos that referenced this issue Mar 4, 2022
bartlettroscoe added a commit to bartlettroscoe/TriBITS that referenced this issue Mar 4, 2022
bartlettroscoe added a commit to bartlettroscoe/TriBITS that referenced this issue Mar 4, 2022
bartlettroscoe added a commit to bartlettroscoe/TriBITS that referenced this issue Mar 4, 2022
bartlettroscoe added a commit to bartlettroscoe/TriBITS that referenced this issue Mar 4, 2022
bartlettroscoe added a commit to bartlettroscoe/TriBITS that referenced this issue Mar 4, 2022
bartlettroscoe added a commit to bartlettroscoe/TriBITS that referenced this issue Mar 4, 2022
bartlettroscoe added a commit to bartlettroscoe/TriBITS that referenced this issue Mar 4, 2022
bartlettroscoe added a commit to bartlettroscoe/TriBITS that referenced this issue Mar 4, 2022
bartlettroscoe added a commit to bartlettroscoe/TriBITS that referenced this issue Mar 4, 2022
bartlettroscoe added a commit to bartlettroscoe/TriBITS that referenced this issue Mar 4, 2022
There were only a few small typos.  Most of the changes were what I consider
improvements to make the text more clear.
bartlettroscoe added a commit that referenced this issue Mar 7, 2022
Better document <Project>_IMPORTED_NO_SYSTEM and workarounds (#443)
@bartlettroscoe
Copy link
Member Author

This is done with the merges of PRs #456 and #459. Moving to done.

TriBITS Refactor automation moved this from In Progress to Done Mar 7, 2022
@bartlettroscoe
Copy link
Member Author

@MikolajZuzek, see this issue. This is where we break backward compatibly.

bartlettroscoe added a commit to bartlettroscoe/Trilinos that referenced this issue Jun 10, 2022
…ty with -isystem include dirs

Hopefully this will be enough for Trilinos users to work through problems with
changes in the handling of include directories.

For more details, see TriBITSPub/TriBITS#443
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Development

No branches or pull requests

4 participants