From f54fde864bb97114a09f4c28148e0cf202eeef4a Mon Sep 17 00:00:00 2001 From: Tim Burke Date: Tue, 28 Feb 2023 16:14:36 -0700 Subject: [PATCH 01/13] Updates for installation logic * Adds basic installation logic - needs more complicated logic for installs with executables/libraries most likely * Adds logic to turn off tests when project is included as a subproject or when BUILD_TESTING (default CTest module option) is turned OFF * Adds interface library libName_testing that links to name and any testing libs (catch-adapter). This enables both installation and testing at the same time as the testing library never needs to be installed * Prevents installed libs from linking to catch-adapter * Prevents finding catch-adapter via FetchContent when tests are turned off * Adds special treatment of spdlog due to namespace issues * Adds config files assuming all dependencies except catch-adapter are installation dependencies --- devtools/build_system.py | 154 +++++++++++++++++++++++--- devtools/dependencies/dependencies.py | 15 ++- devtools/dependencies/dependency.py | 2 +- update_repository.py | 1 + 4 files changed, 151 insertions(+), 21 deletions(-) diff --git a/devtools/build_system.py b/devtools/build_system.py index 3004941..a169a63 100644 --- a/devtools/build_system.py +++ b/devtools/build_system.py @@ -56,6 +56,15 @@ def name(self): else: return os.path.basename(self._path) + @property + def testName(self): + """ The name of the interface library used for testing dependencies, derived from the name + + """ + + return self.name + "_testing" + + @property def dependencies(self): """ List of dependencies. @@ -128,16 +137,25 @@ def write_test_list(self): # Setup ####################################################################### - message( STATUS "Adding {} unit testing" ) - enable_testing() + message( STATUS "Adding {0} unit testing" ) + + add_library( {1} INTERFACE ) + target_link_libraries( {1} INTERFACE {0} ) + """.format(self.name, self.testName)) + ) + + # this expects catch-adapter to be used for the testing library and should be updated if this is replaced with another lib (e.g. Catch2) + if (any(dependency.name == "catch-adapter" for dependency in self.dependencies)): + f.write('target_link_libraries({0} INTERFACE catch-adapter)\n'.format(self.testName)) + f.write(dedent(""" ####################################################################### # Unit testing directories ####################################################################### - """.format(self.name)) - ) + """)) + for dir_ in test_directories: f.write('add_subdirectory( {} )\n'.format(dir_)) @@ -159,9 +177,15 @@ def write_cmakelists(self): ######################################################################## # Preamble ######################################################################## + + set(subproject OFF) + if(DEFINED PROJECT_NAME) + set(subproject ON) + endif() cmake_minimum_required( VERSION 3.14 ) project( {0} LANGUAGES CXX ) + ######################################################################## @@ -171,8 +195,15 @@ def write_cmakelists(self): set( CMAKE_CXX_STANDARD 17 ) set( CMAKE_CXX_STANDARD_REQUIRED YES ) - option( {0}_unit_tests + include(CTest) + include(CMakeDependentOption) + + cmake_dependent_option( {0}_unit_tests "Compile the {0} unit tests and integrate with ctest" ON + BUILD_TESTING AND NOT ${{subproject}} + ) + option( {0}_installation + "Install {0}" ON ) option( strict_compile "Treat all warnings as errors." ON @@ -201,8 +232,6 @@ def write_cmakelists(self): "Options for where to fetch repositories: develop, release, local" ) - message( STATUS "Using ${{REPOSITORIES}} repositories" ) - if( REPOSITORIES STREQUAL "develop" ) include( cmake/develop_dependencies.cmake ) @@ -223,6 +252,12 @@ def write_cmakelists(self): ######################################################################## # Project targets ######################################################################## + include(GNUInstallDirs) + + string( CONCAT prefix + "$" + "$" + ) """) ) @@ -242,18 +277,32 @@ def write_cmakelists(self): else: for file_ in self._tree.list_compiled_source(): f.write('\n {}'.format(file_)) - f.write('\n )\n') + f.write('\n )\n\n') f.write( - 'target_include_directories( {0} {1} src/ )\n' + 'target_include_directories( {0} {1} ${{prefix}} )\n\n' ''.format(self.name, link_type) ) - if self.dependencies: + # see if the dependencies list needs to be written + need_to_write_link_libraries_list = (self.dependencies and + not any(dependency.name == "spdlog" for dependency in self.dependencies) and + not any(dependency.name == "catch-adapter" for dependency in self.dependencies)) + + if need_to_write_link_libraries_list: f.write('target_link_libraries( {}\n'.format(self.name)) for d in self.dependencies: - f.write(' {0} {1}\n'.format(link_type, d.name)) - f.write(' )\n') + if (d.name != "spdlog" and d.name != "catch-adapter"): + f.write(' {0} {1}\n'.format(link_type, d.name)) + f.write(' )\n\n') + + if (any(dependency.name == "spdlog" for dependency in self.dependencies)): + f.write('# treat spdlog specially due to mixed namespace usage\n') + f.write('if (TARGET spdlog::spdlog)\n') + f.write(' target_link_libraries({0} {1} spdlog::spdlog)\n'.format(self.name, link_type)) + f.write('else()\n') + f.write(' target_link_libraries({0} {1} spdlog)\n'.format(self.name, link_type)) + f.write('endif()\n\n') if not self._tree.header_only: f.write(dedent("""\ @@ -277,7 +326,7 @@ def write_cmakelists(self): if( CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR ) # unit testing - if( {}_unit_tests ) + if( ${{{}_unit_tests}} ) include( cmake/unit_testing.cmake ) endif() @@ -285,6 +334,52 @@ def write_cmakelists(self): """.format(self.name)) ) + f.write(dedent("""\ + ####################################################################### + # Installation + ####################################################################### + + if({0}_installation) + include(CMakePackageConfigHelpers) + + install(TARGETS {0} EXPORT {0}-targets + ARCHIVE DESTINATION "${{CMAKE_INSTALL_LIBDIR}}" + LIBRARY DESTINATION "${{CMAKE_INSTALL_LIBDIR}}" + RUNTIME DESTINATION "${{CMAKE_INSTALL_BINDIR}}" + ) + + install(EXPORT {0}-targets + FILE "{0}-targets.cmake" + DESTINATION share/cmake/{0} + ) + + configure_package_config_file( + ${{CMAKE_CURRENT_SOURCE_DIR}}/cmake/{0}-config.cmake.in + ${{CMAKE_BINARY_DIR}}/{0}-config.cmake + INSTALL_DESTINATION share/cmake/{0} + ) + + install(DIRECTORY src/ + DESTINATION "${{CMAKE_INSTALL_INCLUDEDIR}}" + FILES_MATCHING PATTERN "*.hpp" + PATTERN "*test*" EXCLUDE + ) + + install(FILES + "${{PROJECT_BINARY_DIR}}/{0}-config.cmake" + DESTINATION share/cmake/{0} + ) + + if(NOT subproject) + set(CPACK_PACKAGE_VENDOR "Los Alamos National Laboratory") + set(CPACK_RESOURCE_FILE_LICENSE "${{CMAKE_CURRENT_SOURCE_DIR}}/LICENSE") + include(CPack) + endif() + endif() + + """.format(self.name)) + ) + # close file f.close() @@ -312,10 +407,37 @@ def write_dependencies(self): os.path.join( self._path, 'cmake', - filename - ) + filename, + ), + self.name + ) + + def write_installation_dependencies(self): + filename = os.path.join( + self._path, + 'cmake', + "{}-config.cmake.in".format(self.name) ) + with open(filename, 'w') as f: + f.write("include(CMakeFindDependencyMacro)\n\n") + + for d in self.dependencies: + # Don't include catch as an installation dependency + if (d.name != "catch-adapter"): + # Treat spdlog specially since its installed target is namespaced + if (d.name == "spdlog"): + f.write('if (NOT TARGET spdlog::spdlog)\n') + f.write(' find_dependency(spdlog)\n') + f.write('endif()\n\n') + else: + f.write('if (NOT TARGET {0})\n'.format(d.name)) + f.write(' find_dependency({0})\n'.format(d.name)) + f.write('endif()\n\n') + + f.write("""include("${{CMAKE_CURRENT_LIST_DIR}}/{}-targets.cmake")""".format(self.name)) + + ################################################################### # Private functions @@ -394,7 +516,7 @@ def _one_test(self, dir_): target_link_libraries( {0}.test PUBLIC {1} ) - """.format(testname, self.name)) + """.format(testname, self.testName)) ) # compile options diff --git a/devtools/dependencies/dependencies.py b/devtools/dependencies/dependencies.py index 5216b70..80ed0cb 100644 --- a/devtools/dependencies/dependencies.py +++ b/devtools/dependencies/dependencies.py @@ -58,7 +58,7 @@ def add_dependencies(self, *args): 'Cannot register an object other than a Dependency.') self._dependencies.append(dep) - def cmake_file(self, filename): + def cmake_file(self, filename, libName): """ Write the dependency information to a CMake file. """ @@ -95,8 +95,15 @@ def cmake_file(self, filename): """) ) for dependency in self.dependencies: - f.write(' {}\n'.format(dependency.name)) - f.write(' )\n') + if (dependency.name != "catch-adapter"): + f.write(' {}\n'.format(dependency.name)) + f.write(' )\n\n') + + # Only look for testing library if testing is enabled + if (any(dependency.name == "catch-adapter" for dependency in self.dependencies)): + f.write('if (${{{0}_unit_tests}})\n'.format(libName)) + f.write(' FetchContent_MakeAvailable(catch-adapter)\n') + f.write('endif()\n\n') f.close() @@ -106,4 +113,4 @@ def cmake_file(self, filename): d = Dependencies() d.add_dependencies(d1, d2) - d.cmake_file('blah.cmake') + d.cmake_file('blah.cmake', 'libraryName') diff --git a/devtools/dependencies/dependency.py b/devtools/dependencies/dependency.py index aa79c9e..7408f89 100644 --- a/devtools/dependencies/dependency.py +++ b/devtools/dependencies/dependency.py @@ -138,7 +138,7 @@ def fetchcontent_declare(self): """ result = dedent("""\ - FetchContent_Declare( {name} + shacl_FetchContent_Declare( {name} GIT_REPOSITORY {remote} GIT_TAG {tag} """).format( diff --git a/update_repository.py b/update_repository.py index a739aed..b9c8905 100644 --- a/update_repository.py +++ b/update_repository.py @@ -113,6 +113,7 @@ def make_build_system(args): b.dependencies = deps b.write_dependencies() + b.write_installation_dependencies() if not args.release: b.write_cmakelists() b.write_test_list() From af017afc6b68a5552db036b9261a79b83e46a2e6 Mon Sep 17 00:00:00 2001 From: Tim Burke Date: Tue, 28 Feb 2023 16:57:02 -0700 Subject: [PATCH 02/13] Removes code accidentally committed --- devtools/dependencies/dependency.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devtools/dependencies/dependency.py b/devtools/dependencies/dependency.py index 7408f89..aa79c9e 100644 --- a/devtools/dependencies/dependency.py +++ b/devtools/dependencies/dependency.py @@ -138,7 +138,7 @@ def fetchcontent_declare(self): """ result = dedent("""\ - shacl_FetchContent_Declare( {name} + FetchContent_Declare( {name} GIT_REPOSITORY {remote} GIT_TAG {tag} """).format( From b2a9ba9b40c87cdbd95435304cc41b5c03b16be8 Mon Sep 17 00:00:00 2001 From: Tim Burke Date: Tue, 28 Feb 2023 16:58:30 -0700 Subject: [PATCH 03/13] Adds shacl_FetchContent in place of FetchContent, assuming each lib has subtreed shacl::cmake into .cmake --- devtools/build_system.py | 2 +- devtools/dependencies/dependencies.py | 9 +++++---- devtools/dependencies/dependency.py | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/devtools/build_system.py b/devtools/build_system.py index a169a63..4bc42d7 100644 --- a/devtools/build_system.py +++ b/devtools/build_system.py @@ -183,7 +183,7 @@ def write_cmakelists(self): set(subproject ON) endif() - cmake_minimum_required( VERSION 3.14 ) + cmake_minimum_required( VERSION 3.24 ) project( {0} LANGUAGES CXX ) diff --git a/devtools/dependencies/dependencies.py b/devtools/dependencies/dependencies.py index 80ed0cb..e36aecc 100644 --- a/devtools/dependencies/dependencies.py +++ b/devtools/dependencies/dependencies.py @@ -67,8 +67,9 @@ def cmake_file(self, filename, libName): # preamble f.write(dedent("""\ - cmake_minimum_required( VERSION 3.14 ) - include( FetchContent ) + cmake_minimum_required( VERSION 3.24 ) + list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/.cmake) + include( shacl_FetchContent ) """) ) @@ -91,7 +92,7 @@ def cmake_file(self, filename, libName): # Load dependencies ####################################################################### - FetchContent_MakeAvailable( + shacl_FetchContent_MakeAvailable( """) ) for dependency in self.dependencies: @@ -102,7 +103,7 @@ def cmake_file(self, filename, libName): # Only look for testing library if testing is enabled if (any(dependency.name == "catch-adapter" for dependency in self.dependencies)): f.write('if (${{{0}_unit_tests}})\n'.format(libName)) - f.write(' FetchContent_MakeAvailable(catch-adapter)\n') + f.write(' shacl_FetchContent_MakeAvailable(catch-adapter)\n') f.write('endif()\n\n') f.close() diff --git a/devtools/dependencies/dependency.py b/devtools/dependencies/dependency.py index aa79c9e..7408f89 100644 --- a/devtools/dependencies/dependency.py +++ b/devtools/dependencies/dependency.py @@ -138,7 +138,7 @@ def fetchcontent_declare(self): """ result = dedent("""\ - FetchContent_Declare( {name} + shacl_FetchContent_Declare( {name} GIT_REPOSITORY {remote} GIT_TAG {tag} """).format( From 2a333c4ac44adcafbc803c871b74a3eeff23c6db Mon Sep 17 00:00:00 2001 From: Tim Burke Date: Tue, 28 Feb 2023 17:15:10 -0700 Subject: [PATCH 04/13] Updates for using relative URLs for remotes. eigen still uses an absolute URL as it would need to be mirrored or forked into njoy otherwise --- README.md | 4 ++-- dependencies.json | 30 ++++++++++++++--------------- devtools/dependencies/dependency.py | 8 ++++---- 3 files changed, 21 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index a63597a..ec4a91e 100644 --- a/README.md +++ b/README.md @@ -16,9 +16,9 @@ Each supported component's list of dependencies is included in the `dependencies The JSON file requires an entry with the same name as the component, as specified on the command line or inferred from the path. That entry should have a list of dictionaries, each representing a dependency. -Dependencies can be a string, which is taken as the name of the repository, assumed to be on the NJOY project on GitHub and using the default branch. Otherwise, dependencies can be a dictionary, where more options can be specified. +Dependencies can be a string, which is taken as the name of the repository, assumed to be on the NJOY project relative to where the project is hosted and using the default branch. Otherwise, dependencies can be a dictionary, where more options can be specified. -Dependencies specified as a dictionary must include `"name"` or `"remote"`. If `"name"` is included without `"remote"`, the remote is assumed to be `https://github.com/njoy/{name}`. If only `"remote"` is specified, `"name"` is assumed to be the basename of the path. Both can be included, which is necessary when the basename of the remote does not match the name, e.g. dimwits/DimensionalAnalysis. +Dependencies specified as a dictionary must include `"name"` or `"remote"`. If `"name"` is included without `"remote"`, the remote is assumed to be `../../njoy/{name}`. The relative URL capability is provided via [shacl::cmake](https://github.com/shacl/cmake). If only `"remote"` is specified, `"name"` is assumed to be the basename of the path. Both can be included, which is necessary when the basename of the remote does not match the name, e.g. dimwits/DimensionalAnalysis. Including `"tag"` or `"branch"` is optional, and if neither is provided, it defaults to the master branch. If both are provided, an error occurs. This file should include primarily live-at-head dependencies, so specifying a branch is typical. However, perhaps in the case of a third-party dependency or in an overridden dependency file, a specific Git commit hash or Git tag can be used instead. diff --git a/dependencies.json b/dependencies.json index f0351e1..96745e7 100644 --- a/dependencies.json +++ b/dependencies.json @@ -6,19 +6,19 @@ "hana-adapter", "header-utilities", {"name": "range-v3", - "remote": "https://github.com/ericniebler/range-v3", + "remote": "../../ericniebler/range-v3", "tag": "0.11.0"} ], "ACEtk": [ "Log", "catch-adapter", - {"name": "dimwits", "remote": "https://github.com/njoy/DimensionalAnalysis"} , + {"name": "dimwits", "remote": "../../njoy/DimensionalAnalysis"} , "disco", "interpolation", "hana-adapter", "header-utilities", {"name": "range-v3", - "remote": "https://github.com/ericniebler/range-v3", + "remote": "../../ericniebler/range-v3", "tag": "0.11.0"} ], "thermr": [ @@ -33,7 +33,7 @@ ], "Log": [ "catch-adapter", - {"remote": "https://github.com/gabime/spdlog", + {"remote": "../../gabime/spdlog", "tag": "a51b4856377a71f81b6d74b9af459305c4c644f8", "setup": "set( SPDLOG_BUILD_TESTING CACHE BOOL OFF )"} ], @@ -42,14 +42,14 @@ "Log", "pugixml-adapter", {"name": "nlohmann_json", - "remote": "https://github.com/nlohmann/json", + "remote": "../../nlohmann/json", "tag": "v3.7.3", "setup": "set(JSON_BuildTests OFF CACHE INTERNAL \"\")"} ], "NJOY2016": false, "RECONR": [ {"name": "nlohmann_json", - "remote": "https://github.com/nlohmann/json", + "remote": "../../nlohmann/json", "tag": "v3.7.3", "setup": "set(JSON_BuildTests OFF CACHE INTERNAL \"\")"}, {"name": "eigen", @@ -70,7 +70,7 @@ "range-v3-adapter", "catch-adapter", "Log", - {"name": "dimwits", "remote": "https://github.com/njoy/DimensionalAnalysis"} + {"name": "dimwits", "remote": "../../njoy/DimensionalAnalysis"} ], "dimwits": [ "catch-adapter", @@ -89,28 +89,28 @@ ], "interpolation": [ {"name": "range-v3", - "remote": "https://github.com/ericniebler/range-v3", + "remote": "../../ericniebler/range-v3", "tag": "0.11.0"}, "Log", "header-utilities", - {"name": "dimwits", "remote": "https://github.com/njoy/DimensionalAnalysis"} + {"name": "dimwits", "remote": "../../njoy/DimensionalAnalysis"} ], "lipservice": [ "ENDFtk", "catch-adapter", "hana-adapter", {"name": "nlohmann_json", - "remote": "https://github.com/nlohmann/json", + "remote": "../../nlohmann/json", "tag": "v3.7.3", "setup": "set(JSON_BuildTests OFF CACHE INTERNAL \"\")"}, "utility" ], "njoy_c_bindings": [ - {"name": "njoy", "remote": "https://github.com/njoy/NJOY2016"} + {"name": "njoy", "remote": "../../njoy/NJOY2016"} ], "resonanceReconstruction": [ {"name": "range-v3", - "remote": "https://github.com/ericniebler/range-v3", + "remote": "../../ericniebler/range-v3", "tag": "0.11.0"}, "ENDFtk", {"name": "eigen", @@ -118,7 +118,7 @@ "tag": "3.3.8", "setup": "set(BUILD_TESTING OFF CACHE BOOL OFF )"}, "interpolation", - {"name": "dimwits", "remote": "https://github.com/njoy/DimensionalAnalysis"}, + {"name": "dimwits", "remote": "../../njoy/DimensionalAnalysis"}, "elementary" ], "tclap-adapter": false, @@ -134,9 +134,9 @@ "range-v3-adapter": false, "NJOY21": [ "ENDFtk", - {"name": "dimwits", "remote": "https://github.com/njoy/DimensionalAnalysis"} , + {"name": "dimwits", "remote": "../../njoy/DimensionalAnalysis"} , "lipservice", - {"name": "njoy", "remote": "https://github.com/njoy/NJOY2016"}, + {"name": "njoy", "remote": "../../njoy/NJOY2016"}, "njoy_c_bindings", "tclap-adapter", "utility" diff --git a/devtools/dependencies/dependency.py b/devtools/dependencies/dependency.py index 7408f89..79d0051 100644 --- a/devtools/dependencies/dependency.py +++ b/devtools/dependencies/dependency.py @@ -60,10 +60,10 @@ def name(self, value: str): def remote(self): """ The URL to the remote repository location. - This is typically a URL to GitHub, but there are of course - other places one can use. + This is typically a URL relative to where the repository is hosted, but of + course not all repos are hosted on the same server. - If a name is given but not a remote, the NJOY GitHub project + If a name is given but not a remote, the NJOY project is assumed to be the location of the repository. If no name is given, a remote must be provided. @@ -75,7 +75,7 @@ def remote(self): if not self._name: raise Exception( 'Dependency must have name and/or remote defined.') - return 'https://github.com/njoy/{}'.format(self.name) + return '../../njoy/{}'.format(self.name) @remote.setter def remote(self, value: str): From 12af0368aaa224673cc82f97b954e508280244f1 Mon Sep 17 00:00:00 2001 From: Timothy Patrick Burke Date: Tue, 28 Feb 2023 17:33:31 -0700 Subject: [PATCH 05/13] Updates README --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index ec4a91e..232c816 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,8 @@ The script can be executed in one of two modes: develop (default) and release. Dependencies used are specified in the `dependencies.json` files, although this file can be overridden at the command line. +The build system generated by the `update_repositories.py` script requires that the target repository has previously added [shacl::cmake](https://github.com/shacl/cmake) as a repository. + ## Dependency specifications Each supported component's list of dependencies is included in the `dependencies.json` file. Additional components can be added to this list or a user can override the file at the command line. From f4253b7e21c15d0649e5879e61e17c7296a41ab8 Mon Sep 17 00:00:00 2001 From: Tim Burke Date: Wed, 1 Mar 2023 17:50:23 -0700 Subject: [PATCH 06/13] Adds njoy::libName alias and puts installed libs in the njoy:: namespace --- devtools/build_system.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/devtools/build_system.py b/devtools/build_system.py index 4bc42d7..ade0eb9 100644 --- a/devtools/build_system.py +++ b/devtools/build_system.py @@ -279,6 +279,8 @@ def write_cmakelists(self): f.write('\n {}'.format(file_)) f.write('\n )\n\n') + f.write('add_library( njoy::{0} ALIAS {0} )\n\n'.format(self.name)) + f.write( 'target_include_directories( {0} {1} ${{prefix}} )\n\n' ''.format(self.name, link_type) @@ -350,6 +352,7 @@ def write_cmakelists(self): install(EXPORT {0}-targets FILE "{0}-targets.cmake" + NAMESPACE njoy:: DESTINATION share/cmake/{0} ) From 87cf5905c4a33dd5a6de5eb2a2acfeb23204df45 Mon Sep 17 00:00:00 2001 From: Tim Burke Date: Wed, 1 Mar 2023 17:50:23 -0700 Subject: [PATCH 07/13] Adds njoy::libName alias and puts installed libs in the njoy:: namespace --- devtools/build_system.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/devtools/build_system.py b/devtools/build_system.py index a169a63..1e288e8 100644 --- a/devtools/build_system.py +++ b/devtools/build_system.py @@ -279,6 +279,8 @@ def write_cmakelists(self): f.write('\n {}'.format(file_)) f.write('\n )\n\n') + f.write('add_library( njoy::{0} ALIAS {0} )\n\n'.format(self.name)) + f.write( 'target_include_directories( {0} {1} ${{prefix}} )\n\n' ''.format(self.name, link_type) @@ -350,6 +352,7 @@ def write_cmakelists(self): install(EXPORT {0}-targets FILE "{0}-targets.cmake" + NAMESPACE njoy:: DESTINATION share/cmake/{0} ) From f645f5a0bdec6d54d207b1e7ee90be465c47b0fc Mon Sep 17 00:00:00 2001 From: Tim Burke Date: Wed, 1 Mar 2023 18:35:23 -0700 Subject: [PATCH 08/13] Adds ability to handle namespaced install targets and namespaces some njoy dependencies in dependencies.json --- dependencies.json | 57 ++++++++++++++------------- devtools/build_system.py | 9 ++--- devtools/dependencies/dependencies.py | 2 +- devtools/dependencies/dependency.py | 37 +++++++++++++++-- 4 files changed, 67 insertions(+), 38 deletions(-) diff --git a/dependencies.json b/dependencies.json index 96745e7..8406849 100644 --- a/dependencies.json +++ b/dependencies.json @@ -1,23 +1,23 @@ { "ENDFtk": [ - "Log", + "njoy::Log", "catch-adapter", - "disco", + "njoy::disco", "hana-adapter", - "header-utilities", - {"name": "range-v3", + "njoy::header-utilities", + {"name": "range-v3::range-v3", "remote": "../../ericniebler/range-v3", "tag": "0.11.0"} ], "ACEtk": [ - "Log", + "njoy::Log", "catch-adapter", - {"name": "dimwits", "remote": "../../njoy/DimensionalAnalysis"} , - "disco", - "interpolation", + {"name": "njoy::dimwits", "remote": "../../njoy/DimensionalAnalysis"} , + "njoy::disco", + "njoy::interpolation", "hana-adapter", - "header-utilities", - {"name": "range-v3", + "njoy::header-utilities", + {"name": "range-v3::range-v3", "remote": "../../ericniebler/range-v3", "tag": "0.11.0"} ], @@ -33,13 +33,14 @@ ], "Log": [ "catch-adapter", - {"remote": "../../gabime/spdlog", + {"name": "spdlog", + "remote": "../../gabime/spdlog", "tag": "a51b4856377a71f81b6d74b9af459305c4c644f8", "setup": "set( SPDLOG_BUILD_TESTING CACHE BOOL OFF )"} ], "GNDStk": [ "catch-adapter", - "Log", + "njoy::Log", "pugixml-adapter", {"name": "nlohmann_json", "remote": "../../nlohmann/json", @@ -60,17 +61,17 @@ "catch-adapter", "constants", "resonanceReconstruction", - "Log", + "njoy::Log", "twig", - "interpolation", - "elementary" + "njoy::interpolation", + "njoy::elementary" ], "catch-adapter": false, "constants": [ "range-v3-adapter", "catch-adapter", - "Log", - {"name": "dimwits", "remote": "../../njoy/DimensionalAnalysis"} + "njoy::Log", + {"name": "njoy::dimwits", "remote": "../../njoy/DimensionalAnalysis"} ], "dimwits": [ "catch-adapter", @@ -85,15 +86,15 @@ "hana-adapter": false, "header-utilities": [ "catch-adapter", - "Log" + "njoy::Log" ], "interpolation": [ - {"name": "range-v3", + {"name": "range-v3::range-v3", "remote": "../../ericniebler/range-v3", "tag": "0.11.0"}, - "Log", - "header-utilities", - {"name": "dimwits", "remote": "../../njoy/DimensionalAnalysis"} + "njoy::Log", + "njoy::header-utilities", + {"name": "njoy::dimwits", "remote": "../../njoy/DimensionalAnalysis"} ], "lipservice": [ "ENDFtk", @@ -109,7 +110,7 @@ {"name": "njoy", "remote": "../../njoy/NJOY2016"} ], "resonanceReconstruction": [ - {"name": "range-v3", + {"name": "range-v3::range-v3", "remote": "../../ericniebler/range-v3", "tag": "0.11.0"}, "ENDFtk", @@ -117,9 +118,9 @@ "remote": "https://gitlab.com/libeigen/eigen.git", "tag": "3.3.8", "setup": "set(BUILD_TESTING OFF CACHE BOOL OFF )"}, - "interpolation", - {"name": "dimwits", "remote": "../../njoy/DimensionalAnalysis"}, - "elementary" + "njoy::interpolation", + {"name": "njoy::dimwits", "remote": "../../njoy/DimensionalAnalysis"}, + "njoy::elementary" ], "tclap-adapter": false, "eigen-adapter": false, @@ -129,12 +130,12 @@ ], "utility": [ "catch-adapter", - "header-utilities" + "njoy::header-utilities" ], "range-v3-adapter": false, "NJOY21": [ "ENDFtk", - {"name": "dimwits", "remote": "../../njoy/DimensionalAnalysis"} , + {"name": "njoy::dimwits", "remote": "../../njoy/DimensionalAnalysis"} , "lipservice", {"name": "njoy", "remote": "../../njoy/NJOY2016"}, "njoy_c_bindings", diff --git a/devtools/build_system.py b/devtools/build_system.py index ade0eb9..96120b7 100644 --- a/devtools/build_system.py +++ b/devtools/build_system.py @@ -288,14 +288,13 @@ def write_cmakelists(self): # see if the dependencies list needs to be written need_to_write_link_libraries_list = (self.dependencies and - not any(dependency.name == "spdlog" for dependency in self.dependencies) and - not any(dependency.name == "catch-adapter" for dependency in self.dependencies)) + any(dependency.name != "spdlog" and dependency.name != "catch-adapter" for dependency in self.dependencies)) if need_to_write_link_libraries_list: f.write('target_link_libraries( {}\n'.format(self.name)) for d in self.dependencies: if (d.name != "spdlog" and d.name != "catch-adapter"): - f.write(' {0} {1}\n'.format(link_type, d.name)) + f.write(' {0} {1}\n'.format(link_type, d.libName)) f.write(' )\n\n') if (any(dependency.name == "spdlog" for dependency in self.dependencies)): @@ -434,8 +433,8 @@ def write_installation_dependencies(self): f.write(' find_dependency(spdlog)\n') f.write('endif()\n\n') else: - f.write('if (NOT TARGET {0})\n'.format(d.name)) - f.write(' find_dependency({0})\n'.format(d.name)) + f.write('if (NOT TARGET {0})\n'.format(d.libName)) + f.write(' find_dependency({0})\n'.format(d.packageName)) f.write('endif()\n\n') f.write("""include("${{CMAKE_CURRENT_LIST_DIR}}/{}-targets.cmake")""".format(self.name)) diff --git a/devtools/dependencies/dependencies.py b/devtools/dependencies/dependencies.py index e36aecc..7888525 100644 --- a/devtools/dependencies/dependencies.py +++ b/devtools/dependencies/dependencies.py @@ -97,7 +97,7 @@ def cmake_file(self, filename, libName): ) for dependency in self.dependencies: if (dependency.name != "catch-adapter"): - f.write(' {}\n'.format(dependency.name)) + f.write(' {}\n'.format(dependency.packageName)) f.write(' )\n\n') # Only look for testing library if testing is enabled diff --git a/devtools/dependencies/dependency.py b/devtools/dependencies/dependency.py index 79d0051..70d675a 100644 --- a/devtools/dependencies/dependency.py +++ b/devtools/dependencies/dependency.py @@ -39,7 +39,7 @@ def __init__(self, @property def name(self): - """ The name of the repository, as referenced in the build system. + """ The name of the library, as referenced in dependencies.json. Typically, this is unnecessary to set, as the name can be implied from the remote. But, for example, the repostiory @@ -52,6 +52,35 @@ def name(self): else: return os.path.basename(self.remote) + @property + def libName(self): + """ The name of the library, as referenced in the build system. + + This is the name used for linking and is typically namespaced. + E.g. njoy::dimwits + + """ + + if self._name: + return self._name + else: + return "njoy::" + os.path.basename(self.remote) + + @property + def packageName(self): + """ The name of the package, as referenced in the build system. + + This is the name used for calls to find_package and is + either not namespaced or contains the namespace with a dash instead of semicolons. + E.g. dimwits instead of njoy::dimwits. + + """ + + if self._name: + return self._name.split(':')[-1] + else: + return os.path.basename(self.remote) + @name.setter def name(self, value: str): self._name = value @@ -75,7 +104,7 @@ def remote(self): if not self._name: raise Exception( 'Dependency must have name and/or remote defined.') - return '../../njoy/{}'.format(self.name) + return '../../njoy/{}'.format(self.packageName) @remote.setter def remote(self, value: str): @@ -138,11 +167,11 @@ def fetchcontent_declare(self): """ result = dedent("""\ - shacl_FetchContent_Declare( {name} + shacl_FetchContent_Declare( {packageName} GIT_REPOSITORY {remote} GIT_TAG {tag} """).format( - name=self.name, + packageName=self.packageName, remote=self.remote, tag=self.tag ) From ecd1a1769ac34e9be75209470a7cc63f362198e8 Mon Sep 17 00:00:00 2001 From: Tim Burke Date: Thu, 2 Mar 2023 11:58:42 -0700 Subject: [PATCH 09/13] Updates for lowercase-config filename --- devtools/build_system.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/devtools/build_system.py b/devtools/build_system.py index 96120b7..cfd18f8 100644 --- a/devtools/build_system.py +++ b/devtools/build_system.py @@ -354,10 +354,12 @@ def write_cmakelists(self): NAMESPACE njoy:: DESTINATION share/cmake/{0} ) + + string(TOLOWER {0} lowercasePackageName) configure_package_config_file( - ${{CMAKE_CURRENT_SOURCE_DIR}}/cmake/{0}-config.cmake.in - ${{CMAKE_BINARY_DIR}}/{0}-config.cmake + ${{CMAKE_CURRENT_SOURCE_DIR}}/cmake/${{lowercasePackageName}}-config.cmake.in + ${{CMAKE_BINARY_DIR}}/${{lowercasePackageName}}-config.cmake INSTALL_DESTINATION share/cmake/{0} ) @@ -368,7 +370,7 @@ def write_cmakelists(self): ) install(FILES - "${{PROJECT_BINARY_DIR}}/{0}-config.cmake" + "${{PROJECT_BINARY_DIR}}/${{lowercasePackageName}}-config.cmake" DESTINATION share/cmake/{0} ) @@ -418,7 +420,7 @@ def write_installation_dependencies(self): filename = os.path.join( self._path, 'cmake', - "{}-config.cmake.in".format(self.name) + "{}-config.cmake.in".format(self.name.lower()) ) with open(filename, 'w') as f: From 4ffe8e060df9e574412f5f97410b67c3686f854b Mon Sep 17 00:00:00 2001 From: Tim Burke Date: Thu, 2 Mar 2023 13:42:41 -0700 Subject: [PATCH 10/13] Updates how release_dependencies get set - only sets values for the dependencies listed in the json file --- devtools/dependencies/release.py | 80 ++++++-------------------------- update_repository.py | 34 +++++++------- 2 files changed, 31 insertions(+), 83 deletions(-) diff --git a/devtools/dependencies/release.py b/devtools/dependencies/release.py index fc0fd1f..0244c42 100644 --- a/devtools/dependencies/release.py +++ b/devtools/dependencies/release.py @@ -7,7 +7,7 @@ class ReleaseDependencies(Dependencies): - def __init__(self, path): + def __init__(self, deps_path, dependencies): """ Object used to create dependencies for a release based on the currently checked out versions of the dependencies. @@ -16,84 +16,32 @@ def __init__(self, path): Parameters ---------- - path : str + deps_path : str The path to FetchContent's collection of repositories, usually "_deps" in the build folder. + dependencies : Dependencies + The Dependencies constructed from the json file. + """ # call parent constructor Dependencies.__init__(self) - # automatically add dependencies - self._register_existing(path) - - ################################################################### - # Private functions - ################################################################### - - def _register_existing(self, path): - toplevel = os.getcwd() - os.chdir(path) - - for dir_ in sorted(os.listdir(os.getcwd())): - if not dir_.endswith('-src'): - continue - - # enter directory - os.chdir(dir_) - - # name from FetchContent - # unfortunately, case information is lost - fname = dir_[:-4] - - # get remote information - cmd = ['git', 'remote', '-v'] - p = sp.Popen(cmd, stdout=sp.PIPE) - remote = p.communicate()[0].decode().strip() - remote = remote.split()[1] - - # name from remote - # not always the same as name used in the build system - rname = remote.split('/')[-1] - - # resolve names - if fname == rname.lower(): - name = rname - else: - name = fname + for dependency in dependencies: + path = os.path.join( + deps_path, + dependency.packageName.lower() + "-src" + ) - # query git tag - cmd = ['git', 'tag', '--points-at', 'HEAD'] - p = sp.Popen(cmd, stdout=sp.PIPE) - tag = p.communicate()[0].decode().strip() - if tag: - tag = tag.split('\n')[0] + os.chdir(path) - # query git commit cmd = ['git', 'rev-parse', 'HEAD'] p = sp.Popen(cmd, stdout=sp.PIPE) commit = p.communicate()[0].decode().strip() + dependency.tag = commit + self.add_dependencies(dependency) - # construct commit/tag pair - if tag: - commit += ' # tag: {}'.format(tag) - - # register dependency - dep = Dependency( - name=name, - remote=remote, - tag=commit - ) - self.add_dependencies(dep) - - # return from directory - os.chdir('..') - - os.chdir(toplevel) - + os.chdir(toplevel) -if __name__ == '__main__': - d = ReleaseDependencies('../../RECONR/bin/_deps') - d.cmake_file('blah.cmake') diff --git a/update_repository.py b/update_repository.py index b9c8905..8d569d3 100644 --- a/update_repository.py +++ b/update_repository.py @@ -90,27 +90,27 @@ def make_build_system(args): args.name ) + + # Dependencies are given in an input JSON file + with open(args.dependencies, 'r') as f: + dependencies = json.load(f) + + deps = dependencies[b.name] + if deps: + b.dependencies = deps + if args.release: - # Release dependencies are taken from examining the - # build/_deps folder - - b.dependencies = ReleaseDependencies( - os.path.join( - args.path, - args.build_dir, - '_deps' - ) + # Release dependencies are the same as the develop dependencies + # but with the hashes taken from examining the + # checked-out hash in the build/_deps folder + deps_path = os.path.join( + args.path, + args.build_dir, + '_deps' ) + b.dependencies = ReleaseDependencies(deps_path, b.dependencies) - else: - # Develop dependencies are given in an input JSON file - - with open(args.dependencies, 'r') as f: - dependencies = json.load(f) - deps = dependencies[b.name] - if deps: - b.dependencies = deps b.write_dependencies() b.write_installation_dependencies() From e70e374ce2c4503a810626e70801c4e3ed8c42d9 Mon Sep 17 00:00:00 2001 From: Tim Burke Date: Mon, 20 Mar 2023 10:29:02 -0600 Subject: [PATCH 11/13] Fixes bug in build_system - CMAKE_BINARY_DIR -> PROJECT_BINARY_DIR --- devtools/build_system.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devtools/build_system.py b/devtools/build_system.py index cfd18f8..04dafdb 100644 --- a/devtools/build_system.py +++ b/devtools/build_system.py @@ -359,7 +359,7 @@ def write_cmakelists(self): configure_package_config_file( ${{CMAKE_CURRENT_SOURCE_DIR}}/cmake/${{lowercasePackageName}}-config.cmake.in - ${{CMAKE_BINARY_DIR}}/${{lowercasePackageName}}-config.cmake + ${{PROJECT_BINARY_DIR}}/${{lowercasePackageName}}-config.cmake INSTALL_DESTINATION share/cmake/{0} ) From 332ab5d9736c6055081163782fa8df1ca67eb8dd Mon Sep 17 00:00:00 2001 From: Tim Burke Date: Wed, 22 Mar 2023 10:55:02 -0600 Subject: [PATCH 12/13] Reverts release dependencies and only creates install config when creating develop dependencies --- devtools/dependencies/release.py | 80 ++++++++++++++++++++++++++------ update_repository.py | 4 +- 2 files changed, 68 insertions(+), 16 deletions(-) diff --git a/devtools/dependencies/release.py b/devtools/dependencies/release.py index 0244c42..fc0fd1f 100644 --- a/devtools/dependencies/release.py +++ b/devtools/dependencies/release.py @@ -7,7 +7,7 @@ class ReleaseDependencies(Dependencies): - def __init__(self, deps_path, dependencies): + def __init__(self, path): """ Object used to create dependencies for a release based on the currently checked out versions of the dependencies. @@ -16,32 +16,84 @@ def __init__(self, deps_path, dependencies): Parameters ---------- - deps_path : str + path : str The path to FetchContent's collection of repositories, usually "_deps" in the build folder. - dependencies : Dependencies - The Dependencies constructed from the json file. - """ # call parent constructor Dependencies.__init__(self) + # automatically add dependencies + self._register_existing(path) + + ################################################################### + # Private functions + ################################################################### + + def _register_existing(self, path): + toplevel = os.getcwd() - for dependency in dependencies: - path = os.path.join( - deps_path, - dependency.packageName.lower() + "-src" - ) + os.chdir(path) + + for dir_ in sorted(os.listdir(os.getcwd())): + if not dir_.endswith('-src'): + continue + + # enter directory + os.chdir(dir_) + + # name from FetchContent + # unfortunately, case information is lost + fname = dir_[:-4] + + # get remote information + cmd = ['git', 'remote', '-v'] + p = sp.Popen(cmd, stdout=sp.PIPE) + remote = p.communicate()[0].decode().strip() + remote = remote.split()[1] - os.chdir(path) + # name from remote + # not always the same as name used in the build system + rname = remote.split('/')[-1] + # resolve names + if fname == rname.lower(): + name = rname + else: + name = fname + + # query git tag + cmd = ['git', 'tag', '--points-at', 'HEAD'] + p = sp.Popen(cmd, stdout=sp.PIPE) + tag = p.communicate()[0].decode().strip() + if tag: + tag = tag.split('\n')[0] + + # query git commit cmd = ['git', 'rev-parse', 'HEAD'] p = sp.Popen(cmd, stdout=sp.PIPE) commit = p.communicate()[0].decode().strip() - dependency.tag = commit - self.add_dependencies(dependency) - os.chdir(toplevel) + # construct commit/tag pair + if tag: + commit += ' # tag: {}'.format(tag) + + # register dependency + dep = Dependency( + name=name, + remote=remote, + tag=commit + ) + self.add_dependencies(dep) + + # return from directory + os.chdir('..') + + os.chdir(toplevel) + +if __name__ == '__main__': + d = ReleaseDependencies('../../RECONR/bin/_deps') + d.cmake_file('blah.cmake') diff --git a/update_repository.py b/update_repository.py index 8d569d3..7159e83 100644 --- a/update_repository.py +++ b/update_repository.py @@ -108,13 +108,13 @@ def make_build_system(args): args.build_dir, '_deps' ) - b.dependencies = ReleaseDependencies(deps_path, b.dependencies) + b.dependencies = ReleaseDependencies(deps_path) b.write_dependencies() - b.write_installation_dependencies() if not args.release: + b.write_installation_dependencies() b.write_cmakelists() b.write_test_list() From 59b8a7ef706810616a62512dbb82a0f850f31cf1 Mon Sep 17 00:00:00 2001 From: Tim Burke Date: Wed, 22 Mar 2023 11:03:07 -0600 Subject: [PATCH 13/13] Uses develop dependencies by default --- devtools/build_system.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devtools/build_system.py b/devtools/build_system.py index 04dafdb..f043b5d 100644 --- a/devtools/build_system.py +++ b/devtools/build_system.py @@ -227,7 +227,7 @@ def write_cmakelists(self): # Dependencies ######################################################################## - set( REPOSITORIES "release" + set( REPOSITORIES "develop" CACHE STRING "Options for where to fetch repositories: develop, release, local" )