# Struggling to export a project

**URL:** <https://discourse.cmake.org/t/struggling-to-export-a-project/10491>\
**Category:** Code\
**Created:** [March 26, 2024, 5:01pm UTC](https://discourse.cmake.org/t/struggling-to-export-a-project/10491 "2024-03-26T17:01:18Z")\
**Posts on this page:** 6\
**Page:** 1

<div class="post-metadata">

**Author:** ![jwm](https://discourse.cmake.org/user_avatar/discourse.cmake.org/jwm/32/654_2.png) [@jwm](https://discourse.cmake.org/u/jwm)\
**Post date:** [March 26, 2024, 5:01pm UTC](https://discourse.cmake.org/t/struggling-to-export-a-project/10491/1 "2024-03-26T17:01:18Z")

</div>

I hate starting a topic that is so similar to so many others, but I haven’t found a clear explanation of how to get from where I am to where I want to be. I’ve read the [Importing and Exporting Guide](https://cmake.org/cmake/help/latest/guide/importing-exporting/index.html) and all the questions here that were even closely related, and I’m still lost.

I’m currently trying to take some “utility” stuff that I use in every (business, not CMake) project, an package it for general use in the company. Currently, this includes 3 directories, each of which builds a library; some are shared, some are static.

Because I hate copy/pasta, I defined a macro to declare a library such that the specific library really only needs to define its sources (but it’s incomplete). This macro looks like this:

```cmake
macro( DeclLib target linkage )
  
  set(myTarget ${target})
  set(myDepend ${target})
  set(myTest ${target}UnitTest)
  set(myLinkage ${linkage})

  add_library( ${myTarget} ${linkage} )
  cmake_path(GET CMAKE_CURRENT_SOURCE_DIR PARENT_PATH target_inc_dir)
  message(INFO " for target=${target}"
  	       " linkage=${linkage}"
	       " source=${CMAKE_CURRENT_SOURCE_DIR}"
	       " inc=${target_inc_dir}"
	       )

  target_include_directories( ${myTarget} PUBLIC
  			      "$<BUILD_INTERFACE:${target_inc_dir}>"
			      "$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>"
			      )
  file(GLOB target_inc_files
       *.hh *.h *.hpp *.cuh)
### THIS IS PROBLEMATIC ... what is the point of FILE_SET exactly?
# target_sources( ${myTarget} INTERFACE
# FILE_SET inc
# TYPE HEADERS
# BASE_DIRS ${target_inc_dir}
# )

  install(FILES ${target_inc_files}
          DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
  )

  install(TARGETS ${myTarget}
          EXPORT foo
	  LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
	  ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
	  RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
	  INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
	  )
endmacro()

```

If I uncomment out the FILE\_SET, I get an error:

```bash
  install TARGETS target XXX is exported but not all of its interface file
  sets are installed

```

This is on the `install(TARGETS...)` line of the macro, called from each library.

Rather than fight with that, I left it commented out.

After all the subdirs are included, I have:

```cmake
install(EXPORT foo
        FILE foo.cmake
        NAMESPACE foo::
        DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/foo
  )

```

Now, I have an external dependency issue. One of my libraries provides some wrapper around GoogleTest to make it more suitable for our use. This CMakeLists.txt file includes:

```cmake
DeclLib(gtestApp SHARED)

# build the library
target_sources( ${myTarget}
PRIVATE
  ${target_sources}
)
        
target_include_directories( ${myTarget}
PRIVATE
  # this was required or it wouldn't find the "util" include path
  $<TARGET_PROPERTY:util,INTERFACE_INCLUDE_DIRECTORIES>
)

target_link_libraries( ${myTarget} INTERFACE
  util
  gtest
)

```

(GoogleTest is brought in by `FetchContent` in the top-level CMakeLists.txt)

This results in an error:

```bash
CMake Error: install(EXPORT "foo" ...) includes target "gtestApp" which requires target "gtest" that is not in any export set.

```

I don’t want to export **gtest** ; I just want to depend on it. I feel like I’m in a maze of twisty passages, all alike. I’m likely to be eaten by a grue.

First, am I even going in the right direction? Is there a better way to make this available to other projects? My thought is that I’d like for this package to be available using `FetchContent` like any other third party thing.

---

<div class="post-metadata">

**Author:** ![ClausKlein](https://discourse.cmake.org/user_avatar/discourse.cmake.org/clausklein/32/352_2.png) [@ClausKlein](https://discourse.cmake.org/u/ClausKlein)\
**Post date:** [March 26, 2024, 9:15pm UTC](https://discourse.cmake.org/t/struggling-to-export-a-project/10491/2 "2024-03-26T21:15:30Z")

</div>

Do not use INTERFACE for libraries, use either PUBLIC or PRIVATE!

```cmake
find_package(GTest REQUIRED)

include(GNUInstallDirs)

macro(DeclLib target linkage)
  set(myTarget ${target})
  set(myDepend ${target})
  set(myTest ${target}UnitTest) # unused? CK
  set(myLinkage ${linkage})
  set(myFileSet ${target}FileSet)
  set(myExportTargets ${target}Targets)

  add_library(${myTarget} ${linkage})
  cmake_path(GET CMAKE_CURRENT_SOURCE_DIR PARENT_PATH target_inc_dir)

  # NOTE: not needed, done by FILE_SET HEADERS
  # target_include_directories(
  # ${myTarget} PUBLIC "$<BUILD_INTERFACE:${target_inc_dir}>"
  # "$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>"
  # )

  file(GLOB
       target_inc_files
       *.hh
       *.h
       *.hpp
       *.cuh
  )

  message(STATUS
          "target=${target}\n"
          " linkage=${linkage}\n"
          " source=${CMAKE_CURRENT_SOURCE_DIR}\n"
          " include=${target_inc_dir}\n"
          " header=${target_inc_files}"
  )

  # cmake-format: off
  target_sources(
    ${myTarget}
    PUBLIC FILE_SET ${myFileSet}
           TYPE HEADERS
           BASE_DIRS ${target_inc_dir}
           FILES ${target_inc_files}
  )
  # cmake-format: on

 # NO! install(FILES ${target_inc_files} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME})

  install(TARGETS ${myTarget}
          EXPORT ${myExportTargets}
          LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
          ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
          RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
          # NOT used? INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME}
          FILE_SET ${myFileSet} # HEADERS
  )
endmacro()

# build the library
DeclLib(util SHARED)
set(target_sources util.cpp)
target_sources(${myTarget} PRIVATE ${target_sources})
target_link_libraries(${myTarget} PUBLIC GTest::gtest)

```

### but this is not all to export your `cmake config package` in a usable way!

see a working example [cmake/install-rules.cmake](https://github.com/ClausKlein/asio/blob/6686f3c46f91f51241256927e3a16adfd3a44058/asio/cmake/install-rules.cmake)

---

<div class="post-metadata">

**Author:** ![jwm](https://discourse.cmake.org/user_avatar/discourse.cmake.org/jwm/32/654_2.png) [@jwm](https://discourse.cmake.org/u/jwm)\
**Post date:** [April 12, 2024, 1:52am UTC](https://discourse.cmake.org/t/struggling-to-export-a-project/10491/3 "2024-04-12T01:52:00Z")

</div>

Thanks for the detailed response. I have some questions.

> [@ClausKlein](#):
>
> Do not use INTERFACE for libraries, use either PUBLIC or PRIVATE!

Can you explain this? In particular, when I have a static library, and users of my library require another static library, isn’t that precisely what `INTERFACE` is for?

> [@ClausKlein](#):
>
> ```auto
> find_package(GTest REQUIRED)
> 
> ```

I brought in GTest with `FetchContent`. Although the `GTest::gtest` alias target is created, `find_package` didn’t work. I get this error:

```auto
  Could NOT find GTest (missing: GTEST_LIBRARY GTEST_INCLUDE_DIR
  GTEST_MAIN_LIBRARY)

```

Here’s how I bring in GTest:

```cmake
# IMPORTANT: don't inhibit install or CMake will puke
FetchContent_Declare(
  googletest
  GIT_REPOSITORY https://github.com/google/googletest.git
  GIT_TAG f8d7d77c06936315286eb55f8de22cd23c188571 # v1.14.0
  FIND_PACKAGE_ARGS NAMES GTest
)
# The first form is supposed to work, but it doesn't
set(googletest_INSTALL_GTEST ON CACHE INTERNAL "")
set(googletest_BUILD_GMOCK OFF CACHE INTERNAL "")

set(INSTALL_GTEST ON CACHE INTERNAL "")
set(BUILD_GMOCK OFF CACHE INTERNAL "")

FetchContent_MakeAvailable(googletest)

if (NOT TARGET GTest::gtest)
  message(FATAL_ERROR " -- no gtest!")
endif()

```

Since GTest is available in the context of my project, I just removed the `find_package()` command. Am I masking another error?

> [@ClausKlein](#):
>
> ```auto
> EXPORT ${myExportTargets}
> 
> ```

I’m assuming you were trying to infer my intent. Actually, this project has multiple libraries, and I wanted them all installed together. For simplicity, I wrote it as `EXPORT foo`, since I want all libraries included in the same export. Or is your change significant?

> [@ClausKlein](#):
>
> ```auto
> target_link_libraries(${myTarget} PUBLIC GTest::gtest)
> 
> ```

This turned out to be problematic, since GTest was static (and not necessarily PIC). It was easier to make my library static as well. Other than that, what I ended up with is largely like what you wrote. And it all builds. I can even do a `make install` and everything installs properly (surprisingly, it also installs GTest; I fought trying to stop it, but decided it wasn’t worth my time).

NOW…

I’m trying to use **this** repo in another project via `FetchContent`. When it builds, I get the same errors I used to get:

```auto
CMake Error: install(EXPORT "foo" ...) includes target "util" which requires target "gtest" that is not in any export set.

```

I do it this way:

```cmake
include(FetchContent)

# add the google-test unit testing framework
FetchContent_Declare(
  googletest
  GIT_REPOSITORY https://github.com/google/googletest.git
  GIT_TAG f8d7d77c06936315286eb55f8de22cd23c188571 # v1.14.0
  FIND_PACKAGE_ARGS NAMES GTest
)

FetchContent_Declare(
  foo
  GIT_REPOSITORY git@github.com:my-proj/foo.git
  GIT_TAG main
)

FetchContent_MakeAvailable(googletest)
FetchContent_MakeAvailable(foo)

```

(my project is in an Enterprise GitHub, so not publicly visible).

I’ve found `FetchContent` very helpful to use, but I’m failing in providing.

> [@ClausKlein](#):
>
> see a working example [cmake/install-rules.cmake](https://github.com/ClausKlein/asio/blob/6686f3c46f91f51241256927e3a16adfd3a44058/asio/cmake/install-rules.cmake)

I read this and it kind of terrified me. It seems like there is a lot of boilerplate in that file that I might have hoped would be automated. I’m left still wondering if there might be a simpler way to do what I want. My goal is to provide a “package” of helpful code for our other projects. I **can** do the heavy lifting if there is no other way to get there, but I worry who will be able to maintain it when I’m gone.

---

<div class="post-metadata">

**Author:** ![ClausKlein](https://discourse.cmake.org/user_avatar/discourse.cmake.org/clausklein/32/352_2.png) [@ClausKlein](https://discourse.cmake.org/u/ClausKlein)\
**Post date:** [April 12, 2024, 5:04am UTC](https://discourse.cmake.org/t/struggling-to-export-a-project/10491/4 "2024-04-12T05:04:02Z")

</div>

**IMHO** you mix to match things:

- You should not use a macro to build a library
- Do not mix building, testing, installing this way.
- You must not export a test dependency `gtest`, sanitizer, `clang-tidy,` … flags.
- FetchContents may be helpful, but if your library links to the fetched lib, you have to install and export it too.

If you use `find_package()` to import your dependencies, things are much simpler.  
They are already installed 😀

But if it is a shared library, your exported package must check that is still there.  
So you need to use `find_dependency()`

You will find many guides and examples with different solutions for the same problem. Not all of them are good or up to date.

I would start reading here [C++20 Modules, CMake, And Shared Libraries](https://crascit.com/2024/04/04/cxx-modules-cmake-shared-libraries/?unapproved=3877&moderation-hash=47bfd14fbd8d9af4c18384934209e841#comment-3877)

---

<div class="post-metadata">

**Author:** ![ClausKlein](https://discourse.cmake.org/user_avatar/discourse.cmake.org/clausklein/32/352_2.png) [@ClausKlein](https://discourse.cmake.org/u/ClausKlein)\
**Post date:** [April 12, 2024, 5:06am UTC](https://discourse.cmake.org/t/struggling-to-export-a-project/10491/5 "2024-04-12T05:06:53Z")

</div>

> [@jwm](#):
>
> I read this and it kind of terrified me. It seems like there is a lot of boilerplate in that file that I might have hoped would be automated.

This is all generated with [cmake-init](https://github.com/friendlyanon/cmake-init) 🤠

---

<div class="post-metadata">

**Author:** ![jwm](https://discourse.cmake.org/user_avatar/discourse.cmake.org/jwm/32/654_2.png) [@jwm](https://discourse.cmake.org/u/jwm)\
**Post date:** [April 19, 2024, 12:32am UTC](https://discourse.cmake.org/t/struggling-to-export-a-project/10491/6 "2024-04-19T00:32:43Z")

</div>

> [@ClausKlein](#):
>
> - You should not use a macro to build a library

Can you explain why? I am pathological in my commitment to DRY (Don’t Repeat Yourself). The projects I work on are large, with many libraries and executables. A macro seems tailor made to that end. Why is it better to repeat the same boilerplate lines of code in myriad CMakeLists.txt files?

I watched [CppCon 2019: Deep CMake For Library Authors - Crascit](https://crascit.com/2019/10/16/cppcon-2019-deep-cmake-for-library-authors/) and learned a couple of things, but nothing relevant to the issue at hand.

> [@ClausKlein](#):
>
> This is all generated with [cmake-init](https://github.com/friendlyanon/cmake-init) 🤠

I looked at **cmake-init**. The promise of creating `FetchContent`-ready projects is appealing. But, as it says it is “an opinionated CMake project initializer”. I haven’t quite comprehended its opinions.

My code layout is similar to [this one](https://discourse.cmake.org/t/file-set-include-directory-manipulation/10648/6).

In the end, what ultimately solved my issue was to remove `GTest::gtest` from the `target_link_libraries` and just add the include paths like this:

```cmake
if(TARGET GTest::gtest)
  get_target_property(gtest_includes GTest::gtest INCLUDE_DIRECTORIES)
  target_include_directories( ${myTarget} SYSTEM PRIVATE ${gtest_includes})
else()
  message(FATAL_ERROR "GoogleTest must be present to build")
endif()

```

I included them as private because I didn’t want users of this project via `FetchContent` to inherit those paths. This stopped the complaining about not installing GTest.
