diff --git a/.clang-format b/.clang-format
index 05baf03..74f95dc 100644
--- a/.clang-format
+++ b/.clang-format
@@ -125,7 +125,7 @@ IndentCaseBlocks: false
IndentCaseLabels: false
IndentExternBlock: AfterExternBlock
IndentGotoLabels: true
-IndentPPDirectives: None
+IndentPPDirectives: BeforeHash
IndentRequiresClause: true
IndentWidth: 4
IndentWrappedFunctionNames: false
diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
deleted file mode 100644
index b0909de..0000000
--- a/.devcontainer/devcontainer.json
+++ /dev/null
@@ -1,16 +0,0 @@
-// For format details, see https://aka.ms/devcontainer.json. For config options, see the
-// README at: https://github.com/devcontainers/templates/tree/main/src/cpp
-
-{
- "name": "Beman Project Generic Devcontainer",
- "image": "ghcr.io/bemanproject/infra-containers-devcontainer-gcc:14",
- "postCreateCommand": "pre-commit",
- "customizations": {
- "vscode": {
- "extensions": [
- "ms-vscode.cpptools",
- "ms-vscode.cmake-tools"
- ]
- }
- }
-}
diff --git a/.exemplar_version b/.exemplar_version
new file mode 100644
index 0000000..c45b836
--- /dev/null
+++ b/.exemplar_version
@@ -0,0 +1 @@
+f5759898e1ac110fc070791b83267f13c9b50b4c
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index be1083c..c4e1a62 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -1,4 +1,3 @@
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-# Codeowners for reviews on PRs
-* @TedLyngmo @ednolan @JeffGarland @inbal2l
+* @TedLyngmo
diff --git a/.github/workflows/ci_tests.yml b/.github/workflows/ci_tests.yml
index 3c31a20..d1cbc43 100644
--- a/.github/workflows/ci_tests.yml
+++ b/.github/workflows/ci_tests.yml
@@ -9,14 +9,14 @@ on:
pull_request:
workflow_dispatch:
schedule:
- - cron: '30 15 * * *'
+ - cron: '53 16 * * 0'
jobs:
beman-submodule-check:
- uses: bemanproject/infra-workflows/.github/workflows/reusable-beman-submodule-check.yml@1.1.0
+ uses: bemanproject/infra-workflows/.github/workflows/reusable-beman-submodule-check.yml@1.5.0
preset-test:
- uses: bemanproject/infra-workflows/.github/workflows/reusable-beman-preset-test.yml@1.1.0
+ uses: bemanproject/infra-workflows/.github/workflows/reusable-beman-preset-test.yml@1.5.0
with:
matrix_config: >
[
@@ -31,7 +31,7 @@ jobs:
]
build-and-test:
- uses: bemanproject/infra-workflows/.github/workflows/reusable-beman-build-and-test.yml@1.1.0
+ uses: bemanproject/infra-workflows/.github/workflows/reusable-beman-build-and-test.yml@1.5.0
with:
matrix_config: >
{
@@ -143,4 +143,4 @@ jobs:
create-issue-when-fault:
needs: [preset-test, build-and-test]
if: failure() && github.event_name == 'schedule'
- uses: bemanproject/infra-workflows/.github/workflows/reusable-beman-create-issue-when-fault.yml@1.1.0
+ uses: bemanproject/infra-workflows/.github/workflows/reusable-beman-create-issue-when-fault.yml@1.5.0
diff --git a/.github/workflows/pre-commit-check.yml b/.github/workflows/pre-commit-check.yml
index 70895b4..5d1ca93 100644
--- a/.github/workflows/pre-commit-check.yml
+++ b/.github/workflows/pre-commit-check.yml
@@ -8,6 +8,12 @@ on:
branches:
- main
+permissions:
+ contents: read
+ checks: write
+ issues: write
+ pull-requests: write
+
jobs:
pre-commit:
- uses: bemanproject/infra-workflows/.github/workflows/reusable-beman-pre-commit.yml@1.1.0
+ uses: bemanproject/infra-workflows/.github/workflows/reusable-beman-pre-commit.yml@1.5.0
diff --git a/.github/workflows/pre-commit-update.yml b/.github/workflows/pre-commit-update.yml
index ec7ac74..bc842b2 100644
--- a/.github/workflows/pre-commit-update.yml
+++ b/.github/workflows/pre-commit-update.yml
@@ -5,11 +5,11 @@ name: Weekly pre-commit autoupdate
on:
workflow_dispatch:
schedule:
- - cron: "0 16 * * 0"
+ - cron: "5 15 * * 3"
jobs:
auto-update-pre-commit:
- uses: bemanproject/infra-workflows/.github/workflows/reusable-beman-update-pre-commit.yml@1.1.0
+ uses: bemanproject/infra-workflows/.github/workflows/reusable-beman-update-pre-commit.yml@1.5.0
secrets:
APP_ID: ${{ secrets.AUTO_PR_BOT_APP_ID }}
PRIVATE_KEY: ${{ secrets.AUTO_PR_BOT_PRIVATE_KEY }}
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 4591d9e..6836494 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,13 +1,13 @@
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-cmake_minimum_required(VERSION 3.28)
+cmake_minimum_required(VERSION 3.30...4.3)
project(
beman.timed_lock_alg # CMake Project Name, which is also the name of the top-level
# targets (e.g., library, executable, etc.).
- DESCRIPTION "Beman Timed Lock Algorithms"
+ DESCRIPTION "Timed lock algorithms for multiple lockables"
LANGUAGES CXX
- VERSION 1.0.0
+ VERSION 0.1.0
)
# [CMAKE.SKIP_TESTS]
@@ -24,11 +24,29 @@ option(
${PROJECT_IS_TOP_LEVEL}
)
-include(CTest)
+# for find of beman-install-library
+include(infra/cmake/beman-install-library.cmake)
+add_library(beman.timed_lock_alg)
+add_library(beman::timed_lock_alg ALIAS beman.timed_lock_alg)
+
+target_sources(
+ beman.timed_lock_alg
+ PUBLIC FILE_SET HEADERS BASE_DIRS "${CMAKE_CURRENT_SOURCE_DIR}/include"
+)
+
+set_target_properties(
+ beman.timed_lock_alg
+ PROPERTIES VERIFY_INTERFACE_HEADER_SETS ${PROJECT_IS_TOP_LEVEL}
+)
+
+add_subdirectory(include/beman/timed_lock_alg)
add_subdirectory(src/beman/timed_lock_alg)
+beman_install_library(beman.timed_lock_alg TARGETS beman.timed_lock_alg)
+
if(BEMAN_TIMED_LOCK_ALG_BUILD_TESTS)
+ enable_testing()
add_subdirectory(tests/beman/timed_lock_alg)
endif()
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..74fddc2
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,111 @@
+# Development
+
+## Configure and Build the Project Using CMake Presets
+
+The simplest way of configuring and building the project is to use [CMake
+Presets](https://cmake.org/cmake/help/latest/manual/cmake-presets.7.html). Appropriate
+presets for major compilers have been included by default. You can use `cmake
+--list-presets=workflow` to see all available presets.
+
+Here is an example of invoking the `gcc-debug` preset:
+
+```shell
+cmake --workflow --preset gcc-debug
+```
+
+Generally, there are two kinds of presets, `debug` and `release`.
+
+The `debug` presets are designed to aid development, so they have debuginfo and sanitizers
+enabled.
+
+> [!NOTE]
+>
+> The sanitizers that are enabled vary from compiler to compiler. See the toolchain files
+> under ([`infra/cmake`](infra/cmake/)) to determine the exact configuration used for each
+> preset.
+
+The `release` presets are designed for production use, and
+consequently have the highest optimization turned on (e.g. `O3`).
+
+## Configure and Build Manually
+
+If the presets are not suitable for your use case, a traditional CMake invocation will
+provide more configurability.
+
+To configure, build and test the project manually, you can run this set of commands. Note
+that this requires GoogleTest to be installed.
+
+```bash
+cmake \
+ -B build \
+ -S . \
+ -DCMAKE_CXX_STANDARD=20 \
+ # Your extra arguments here.
+cmake --build build
+ctest --test-dir build
+```
+
+> [!IMPORTANT]
+>
+> Beman projects are [passive projects](
+> https://github.com/bemanproject/beman/blob/main/docs/beman_standard.md#cmakepassive_projects),
+> so you need to specify the C++ version via `CMAKE_CXX_STANDARD` when manually
+> configuring the project.
+
+## Dependency Management
+
+### FetchContent
+
+Instead of installing the project's dependencies via a package manager, you can optionally
+configure beman.timed_lock_alg to fetch them automatically via CMake FetchContent.
+
+To do so, specify
+`-DCMAKE_PROJECT_TOP_LEVEL_INCLUDES=./infra/cmake/use-fetch-content.cmake`. This will
+bring in GoogleTest automatically along with any other dependency the project may require.
+
+Example commands:
+
+```shell
+cmake \
+ -B build \
+ -S . \
+ -DCMAKE_CXX_STANDARD=20 \
+ -DCMAKE_PROJECT_TOP_LEVEL_INCLUDES=./infra/cmake/use-fetch-content.cmake
+cmake --build build
+ctest --test-dir build
+```
+
+The file `./lockfile.json` configures the list of dependencies and versions that will be
+acquired by FetchContent.
+
+## Project-specific configure arguments
+
+Project-specific options are prefixed with `BEMAN_TIMED_LOCK_ALG`.
+You can see the list of available options with:
+
+```bash
+cmake -LH -S . -B build | grep "BEMAN_TIMED_LOCK_ALG" -C 2
+```
+
+
+
+Some project-specific configure arguments
+
+### `BEMAN_TIMED_LOCK_ALG_BUILD_TESTS`
+
+Enable building tests and test infrastructure. Default: `ON`.
+Values: `{ ON, OFF }`.
+
+### `BEMAN_TIMED_LOCK_ALG_BUILD_EXAMPLES`
+
+Enable building examples. Default: `ON`. Values: `{ ON, OFF }`.
+
+### `BEMAN_TIMED_LOCK_ALG_INSTALL_CONFIG_FILE_PACKAGE`
+
+Enable installing the CMake config file package. Default: `ON`.
+Values: `{ ON, OFF }`.
+
+This is required so that users of `beman.timed_lock_alg` can use
+`find_package(beman.timed_lock_alg)` to locate the library.
+
+
diff --git a/README.md b/README.md
index b1dd770..b7de3d9 100644
--- a/README.md
+++ b/README.md
@@ -1,11 +1,11 @@
-# beman.timed_lock_alg : Timed lock algorithms for multiple lockables
+# beman.timed_lock_alg: Timed lock algorithms for multiple lockables
-   [](https://coveralls.io/github/bemanproject/timed_lock_alg?branch=main)  [](https://godbolt.org/z/jPYdxT3E7)
+   [](https://coveralls.io/github/bemanproject/timed_lock_alg?branch=main)  [](https://godbolt.org/z/jPYdxT3E7)
`beman.timed_lock_alg` implements timed lock algorithms for multiple lockables and `std::multi_lock`.
@@ -22,7 +22,8 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
`std::try_lock_until` and `std::try_lock_for` are a function templates usable with zero to many _TimedLockables_.
Example:
-```
+
+```cpp
std::timed_mutex m1, m2;
if (std::try_lock_for(100ms, m1, m2) == -1) {
// success
@@ -36,7 +37,8 @@ if (std::try_lock_for(100ms, m1, m2) == -1) {
`std::multi_lock` is a flexible RAII container usable with zero to many _BasicLockables_.
Example:
-```
+
+```cpp
std::timed_mutex m1, m2;
std::multi_lock lock(100ms, m1, m2);
if (lock) {
@@ -53,12 +55,11 @@ Full runnable examples can be found in [`examples/`](examples/).
This project requires at least the following to build:
* A C++ compiler that conforms to the C++20 standard or greater
-* CMake 3.28 or later
+* CMake 3.30 or later
* (Test Only) GoogleTest
-You can disable building tests by setting CMake option
-[`BEMAN_TIMED_LOCK_ALG_BUILD_TESTS`](#beman_timed_lock_alg_build_tests) to `OFF`
-when configuring the project.
+You can disable building tests by setting CMake option `BEMAN_TIMED_LOCK_ALG_BUILD_TESTS` to
+`OFF` when configuring the project.
### Supported Platforms
@@ -80,6 +81,8 @@ This project also supports ICX (with libstdc++ or libc++) versions 2021.1.2-2025
## Development
+See the [Contributing Guidelines](CONTRIBUTING.md).
+
### Develop using GitHub Codespace
This project supports [GitHub Codespace](https://github.com/features/codespaces)
@@ -257,7 +260,6 @@ cmake -B build -S . -DCMAKE_CXX_STANDARD=20 -DBEMAN_TIMED_LOCK_ALG_BUILD_TESTS=O
Enable building examples. Default: ON. Values: { ON, OFF }.
-
#### `BEMAN_TIMED_LOCK_ALG_INSTALL_CONFIG_FILE_PACKAGE`
Enable installing the CMake config file package. Default: ON.
@@ -270,35 +272,87 @@ This is required so that users of `beman.timed_lock_alg` can use
## Integrate beman.timed_lock_alg into your project
-To use `beman.timed_lock_alg` in your C++ project,
-include an appropriate `beman.timed_lock_alg` header from your source code.
+### Build
-```c++
-#include
+You can build timed_lock_alg using a CMake workflow preset:
+
+```bash
+cmake --workflow --preset gcc-release
```
-> [!NOTE]
->
-> `beman.timed_lock_alg` headers are to be included with the `beman/timed_lock_alg/` prefix.
-> Altering include search paths to spell the include target another way (e.g.
-> `#include `) is unsupported.
+To list available workflow presets, you can invoke:
+
+```bash
+cmake --list-presets=workflow
+```
+
+For details on building beman.timed_lock_alg without using a CMake preset, refer to the
+[Contributing Guidelines](CONTRIBUTING.md).
+
+### Installation
+
+To install beman.timed_lock_alg globally after building with the `gcc-release` preset, you can
+run:
-The process for incorporating `beman.timed_lock_alg` into your project depends on the
-build system being used. Instructions for CMake are provided in following sections.
+```bash
+sudo cmake --install build/gcc-release
+```
+
+Alternatively, to install to a prefix, for example `/opt/beman`, you can run:
+
+```bash
+sudo cmake --install build/gcc-release --prefix /opt/beman
+```
-### Incorporating `beman.timed_lock_alg` into your project with CMake
+This will generate the following directory structure:
+
+```txt
+/opt/beman
+├── include
+│ └── beman
+│ └── timed_lock_alg
+│ ├── timed_lock_alg.hpp
+│ └── ...
+└── lib
+ ├── cmake
+ │ └── beman.timed_lock_alg
+ │ ├── beman.timed_lock_alg-config-version.cmake
+ │ ├── beman.timed_lock_alg-config.cmake
+ │ ├── beman.timed_lock_alg-targets-debug.cmake
+ │ └── beman.timed_lock_alg-targets.cmake
+ └── libbeman.timed_lock_alg.a
+```
+
+### CMake Configuration
+
+If you installed beman.timed_lock_alg to a prefix, you can specify that prefix to your CMake
+project using `CMAKE_PREFIX_PATH`; for example, `-DCMAKE_PREFIX_PATH=/opt/beman`.
-For CMake based projects,
-you will need to use the `beman.timed_lock_alg` CMake module
-to define the `beman::timed_lock_alg` CMake target:
+You need to bring in the `beman.timed_lock_alg` package to define the `beman::timed_lock_alg` CMake
+target:
```cmake
find_package(beman.timed_lock_alg REQUIRED)
```
-You will also need to add `beman::timed_lock_alg` to the link libraries of
-any libraries or executables that include `beman.timed_lock_alg` headers.
+You will then need to add `beman::timed_lock_alg` to the link libraries of any libraries or
+executables that include `beman.timed_lock_alg` headers.
```cmake
target_link_libraries(yourlib PUBLIC beman::timed_lock_alg)
```
+
+### Using beman.timed_lock_alg
+
+To use `beman.timed_lock_alg` in your C++ project,
+include an appropriate `beman.timed_lock_alg` header from your source code.
+
+```c++
+#include
+```
+
+> [!NOTE]
+>
+> `beman.timed_lock_alg` headers are to be included with the `beman/timed_lock_alg/` prefix.
+> Altering include search paths to spell the include target another way (e.g.
+> `#include `) is unsupported.
diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt
index 51ca24c..9ffe8e3 100644
--- a/examples/CMakeLists.txt
+++ b/examples/CMakeLists.txt
@@ -1,7 +1,6 @@
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
set(ALL_EXAMPLES try_lock_a_bunch try_lock_until_success)
-
message("Examples to be built: ${ALL_EXAMPLES}")
foreach(example ${ALL_EXAMPLES})
diff --git a/include/beman/timed_lock_alg/CMakeLists.txt b/include/beman/timed_lock_alg/CMakeLists.txt
new file mode 100644
index 0000000..1941656
--- /dev/null
+++ b/include/beman/timed_lock_alg/CMakeLists.txt
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+target_sources(
+ beman.timed_lock_alg
+ PUBLIC FILE_SET HEADERS FILES timed_lock_alg.hpp mutex.hpp
+)
diff --git a/include/beman/timed_lock_alg/timed_lock_alg.hpp b/include/beman/timed_lock_alg/timed_lock_alg.hpp
new file mode 100644
index 0000000..b8fd0bc
--- /dev/null
+++ b/include/beman/timed_lock_alg/timed_lock_alg.hpp
@@ -0,0 +1,8 @@
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+#ifndef BEMAN_TIMED_LOCK_ALG_TIMED_LOCK_ALG_HPP
+#define BEMAN_TIMED_LOCK_ALG_TIMED_LOCK_ALG_HPP
+
+#include
+
+#endif // BEMAN_TIMED_LOCK_ALG_TIMED_LOCK_ALG_HPP
diff --git a/infra/.beman_submodule b/infra/.beman_submodule
index bfed167..424d64e 100644
--- a/infra/.beman_submodule
+++ b/infra/.beman_submodule
@@ -1,3 +1,3 @@
[beman_submodule]
remote=https://github.com/bemanproject/infra.git
-commit_hash=bb58b2a1cc894d58a55bf745be78f5d27029e245
+commit_hash=54dcdad8b661a405a6ac06453f0f06da5d30ba5c
diff --git a/infra/.github/workflows/beman-submodule.yml b/infra/.github/workflows/beman-submodule.yml
deleted file mode 100644
index 8435086..0000000
--- a/infra/.github/workflows/beman-submodule.yml
+++ /dev/null
@@ -1,32 +0,0 @@
-# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-
-name: beman-submodule tests
-
-on:
- push:
- branches:
- - main
- pull_request:
- workflow_dispatch:
-
-jobs:
- beman-submodule-script-ci:
- name: beman_module.py ci
- runs-on: ubuntu-latest
- steps:
- - name: Checkout repository
- uses: actions/checkout@v4
-
- - name: Set up Python
- uses: actions/setup-python@v5
- with:
- python-version: 3.13
-
- - name: Install pytest
- run: |
- python3 -m pip install pytest
-
- - name: Run pytest
- run: |
- cd tools/beman-submodule/
- pytest
diff --git a/infra/.pre-commit-config.yaml b/infra/.pre-commit-config.yaml
index e806e59..bc4dd84 100644
--- a/infra/.pre-commit-config.yaml
+++ b/infra/.pre-commit-config.yaml
@@ -19,14 +19,3 @@ repos:
- id: gersemi
name: CMake linting
exclude: ^.*/tests/.*/data/ # Exclude test data directories
-
- # Python linting and formatting
- # config file: ruff.toml (not currently present but add if needed)
- # https://docs.astral.sh/ruff/configuration/
- - repo: https://github.com/astral-sh/ruff-pre-commit
- rev: v0.13.2
- hooks:
- - id: ruff-check
- files: ^tools/beman-tidy/
- - id: ruff-format
- files: ^tools/beman-tidy/
diff --git a/infra/.pre-commit-hooks.yaml b/infra/.pre-commit-hooks.yaml
deleted file mode 100644
index d327587..0000000
--- a/infra/.pre-commit-hooks.yaml
+++ /dev/null
@@ -1,7 +0,0 @@
-- id: beman-tidy
- name: "beman-tidy: bemanification your repo"
- entry: ./tools/beman-tidy/beman-tidy
- language: script
- pass_filenames: false
- always_run: true
- args: [".", "--verbose"]
diff --git a/infra/README.md b/infra/README.md
index 16b2672..a869e46 100644
--- a/infra/README.md
+++ b/infra/README.md
@@ -9,12 +9,11 @@ so it does not respect the usual structure of a Beman library repository nor The
* `cmake/`: CMake modules and toolchain files used by Beman libraries.
* `containers/`: Containers used for CI builds and tests in the Beman org.
-* `tools/`: Tools used to manage the infrastructure and the codebase (e.g., linting, formatting, etc.).
## Usage
This repository is intended to be used as a beman-submodule in other Beman repositories. See
-[the Beman Submodule documentation](./tools/beman-submodule/README.md) for details.
+[the beman-submodule documentation](https://github.com/bemanproject/beman-submodule) for details.
### CMake Modules
diff --git a/infra/cmake/Config.cmake.in b/infra/cmake/Config.cmake.in
new file mode 100644
index 0000000..81adf80
--- /dev/null
+++ b/infra/cmake/Config.cmake.in
@@ -0,0 +1,12 @@
+# cmake/Config.cmake.in -*-makefile-*-
+# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+include(CMakeFindDependencyMacro)
+
+@BEMAN_FIND_DEPENDENCIES@
+
+@PACKAGE_INIT@
+
+include(${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@-targets.cmake)
+
+check_required_components(@PROJECT_NAME@)
diff --git a/infra/cmake/beman-install-library-config.cmake b/infra/cmake/beman-install-library-config.cmake
deleted file mode 100644
index e7fd0ad..0000000
--- a/infra/cmake/beman-install-library-config.cmake
+++ /dev/null
@@ -1,169 +0,0 @@
-# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-include_guard(GLOBAL)
-
-# This file defines the function `beman_install_library` which is used to
-# install a library target and its headers, along with optional CMake
-# configuration files.
-#
-# The function is designed to be reusable across different Beman libraries.
-
-function(beman_install_library name)
- # Usage
- # -----
- #
- # beman_install_library(NAME)
- #
- # Brief
- # -----
- #
- # This function installs the specified library target and its headers.
- # It also handles the installation of the CMake configuration files if needed.
- #
- # CMake variables
- # ---------------
- #
- # Note that configuration of the installation is generally controlled by CMake
- # cache variables so that they can be controlled by the user or tool running the
- # `cmake` command. Neither `CMakeLists.txt` nor `*.cmake` files should set these
- # variables directly.
- #
- # - BEMAN_INSTALL_CONFIG_FILE_PACKAGES:
- # List of packages that require config file installation.
- # If the package name is in this list, it will install the config file.
- #
- # - _INSTALL_CONFIG_FILE_PACKAGE:
- # Boolean to control config file installation for the specific library.
- # The prefix `` is the uppercased name of the library with dots
- # replaced by underscores.
- #
- if(NOT TARGET "${name}")
- message(FATAL_ERROR "Target '${name}' does not exist.")
- endif()
-
- if(NOT ARGN STREQUAL "")
- message(
- FATAL_ERROR
- "beman_install_library does not accept extra arguments: ${ARGN}"
- )
- endif()
-
- # Given foo.bar, the component name is bar
- string(REPLACE "." ";" name_parts "${name}")
- # fail if the name doesn't look like foo.bar
- list(LENGTH name_parts name_parts_length)
- if(NOT name_parts_length EQUAL 2)
- message(
- FATAL_ERROR
- "beman_install_library expects a name of the form 'beman.', got '${name}'"
- )
- endif()
-
- set(target_name "${name}")
- set(install_component_name "${name}")
- set(export_name "${name}")
- set(package_name "${name}")
- list(GET name_parts -1 component_name)
-
- install(
- TARGETS "${target_name}"
- COMPONENT "${install_component_name}"
- EXPORT "${export_name}"
- FILE_SET HEADERS
- )
-
- set_target_properties(
- "${target_name}"
- PROPERTIES EXPORT_NAME "${component_name}"
- )
-
- include(GNUInstallDirs)
-
- # Determine the prefix for project-specific variables
- string(TOUPPER "${name}" project_prefix)
- string(REPLACE "." "_" project_prefix "${project_prefix}")
-
- option(
- ${project_prefix}_INSTALL_CONFIG_FILE_PACKAGE
- "Enable building examples. Default: ${PROJECT_IS_TOP_LEVEL}. Values: { ON, OFF }."
- ${PROJECT_IS_TOP_LEVEL}
- )
-
- # By default, install the config package
- set(install_config_package ON)
-
- # Turn OFF installation of config package by default if,
- # in order of precedence:
- # 1. The specific package variable is set to OFF
- # 2. The package name is not in the list of packages to install config files
- if(DEFINED BEMAN_INSTALL_CONFIG_FILE_PACKAGES)
- if(
- NOT "${install_component_name}"
- IN_LIST
- BEMAN_INSTALL_CONFIG_FILE_PACKAGES
- )
- set(install_config_package OFF)
- endif()
- endif()
- if(DEFINED ${project_prefix}_INSTALL_CONFIG_FILE_PACKAGE)
- set(install_config_package
- ${${project_prefix}_INSTALL_CONFIG_FILE_PACKAGE}
- )
- endif()
-
- if(install_config_package)
- message(
- DEBUG
- "beman-install-library: Installing a config package for '${name}'"
- )
-
- include(CMakePackageConfigHelpers)
-
- find_file(
- config_file_template
- NAMES "${package_name}-config.cmake.in"
- PATHS "${CMAKE_CURRENT_SOURCE_DIR}"
- NO_DEFAULT_PATH
- NO_CACHE
- REQUIRED
- )
- set(config_package_file
- "${CMAKE_CURRENT_BINARY_DIR}/${package_name}-config.cmake"
- )
- set(package_install_dir "${CMAKE_INSTALL_LIBDIR}/cmake/${package_name}")
- configure_package_config_file(
- "${config_file_template}"
- "${config_package_file}"
- INSTALL_DESTINATION "${package_install_dir}"
- PATH_VARS PROJECT_NAME PROJECT_VERSION
- )
-
- set(config_version_file
- "${CMAKE_CURRENT_BINARY_DIR}/${package_name}-config-version.cmake"
- )
- write_basic_package_version_file(
- "${config_version_file}"
- VERSION "${PROJECT_VERSION}"
- COMPATIBILITY ExactVersion
- )
-
- install(
- FILES "${config_package_file}" "${config_version_file}"
- DESTINATION "${package_install_dir}"
- COMPONENT "${install_component_name}"
- )
-
- set(config_targets_file "${package_name}-targets.cmake")
- install(
- EXPORT "${export_name}"
- DESTINATION "${package_install_dir}"
- NAMESPACE beman::
- FILE "${config_targets_file}"
- COMPONENT "${install_component_name}"
- )
- else()
- message(
- DEBUG
- "beman-install-library: Not installing a config package for '${name}'"
- )
- endif()
-endfunction()
diff --git a/infra/cmake/beman-install-library.cmake b/infra/cmake/beman-install-library.cmake
new file mode 100644
index 0000000..a77ee1a
--- /dev/null
+++ b/infra/cmake/beman-install-library.cmake
@@ -0,0 +1,323 @@
+# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+include_guard(GLOBAL)
+
+include(CMakePackageConfigHelpers)
+include(GNUInstallDirs)
+
+# beman_install_library
+# =====================
+#
+# Installs a library (or set of targets) along with headers, C++ modules,
+# and optional CMake package configuration files.
+#
+# Usage:
+# ------
+# beman_install_library(
+# TARGETS [ ...]
+# [DEPENDENCIES [ ...]]
+# [NAMESPACE ]
+# [EXPORT_NAME ]
+# [DESTINATION ]
+# )
+#
+# Arguments:
+# ----------
+#
+# name
+# Logical package name (e.g. "beman.utility").
+# Used to derive config file names and cache variable prefixes.
+#
+# TARGETS (required)
+# List of CMake targets to install.
+#
+# DEPENDENCIES (optional)
+# Semicolon-separated list, one dependency per entry.
+# Each entry is a valid find_dependency() argument list.
+# Note: you must use the bracket form for quoting if not only a package name is used!
+# "[===[beman.inplace_vector 1.0.0]===] [===[beman.scope 0.0.1 EXACT]===] fmt"
+#
+# NAMESPACE (optional)
+# Namespace for exported targets.
+# Defaults to "beman::".
+#
+# EXPORT_NAME (optional)
+# Name of the CMake export set.
+# Defaults to "-targets".
+#
+# DESTINATION (optional)
+# The install destination for CXX_MODULES.
+# Defaults to ${CMAKE_INSTALL_LIBDIR}/cmake/${name}/modules.
+#
+# Brief
+# -----
+#
+# This function installs the specified project TARGETS and its FILE_SET
+# HEADERS to the default CMAKE install destination.
+#
+# It also handles the installation of the CMake config package files if
+# needed. If the given targets has a PUBLIC FILE_SET CXX_MODULE, it will also
+# installed to the given DESTINATION
+#
+# Cache variables:
+# ----------------
+#
+# BEMAN_INSTALL_CONFIG_FILE_PACKAGES
+# List of package names for which config files should be installed.
+#
+# _INSTALL_CONFIG_FILE_PACKAGE
+# Per-package override to enable/disable config file installation.
+# is the uppercased package name with dots replaced by underscores.
+#
+# Caveats
+# -------
+#
+# **Only one `PUBLIC FILE_SET CXX_MODULES` is yet supported to install with this
+# function!**
+#
+# **Only header files contained in a `PUBLIC FILE_SET TYPE HEADERS` will be
+# install with this function!**
+
+function(beman_install_library name)
+ # ----------------------------
+ # Argument parsing
+ # ----------------------------
+ set(oneValueArgs NAMESPACE EXPORT_NAME DESTINATION)
+ set(multiValueArgs TARGETS DEPENDENCIES)
+
+ cmake_parse_arguments(
+ BEMAN
+ "${options}"
+ "${oneValueArgs}"
+ "${multiValueArgs}"
+ ${ARGN}
+ )
+
+ if(NOT BEMAN_TARGETS)
+ message(
+ FATAL_ERROR
+ "beman_install_library(${name}): TARGETS must be specified"
+ )
+ endif()
+
+ if(CMAKE_SKIP_INSTALL_RULES)
+ message(
+ WARNING
+ "beman_install_library(${name}): not installing targets '${BEMAN_TARGETS}' due to CMAKE_SKIP_INSTALL_RULES"
+ )
+ return()
+ endif()
+
+ set(_config_install_dir "${CMAKE_INSTALL_LIBDIR}/cmake/${name}")
+
+ # ----------------------------
+ # Defaults
+ # ----------------------------
+ if(NOT BEMAN_NAMESPACE)
+ set(BEMAN_NAMESPACE "beman::")
+ endif()
+
+ if(NOT BEMAN_EXPORT_NAME)
+ set(BEMAN_EXPORT_NAME "${name}-targets")
+ endif()
+
+ if(NOT BEMAN_DESTINATION)
+ set(BEMAN_DESTINATION "${_config_install_dir}/modules")
+ endif()
+
+ string(REPLACE "beman." "" install_component_name "${name}")
+ message(
+ VERBOSE
+ "beman-install-library(${name}): COMPONENT '${install_component_name}'"
+ )
+
+ # --------------------------------------------------
+ # Install each target with all of its file sets
+ # --------------------------------------------------
+ foreach(_tgt IN LISTS BEMAN_TARGETS)
+ if(NOT TARGET "${_tgt}")
+ message(
+ WARNING
+ "beman_install_library(${name}): '${_tgt}' is not a target"
+ )
+ continue()
+ endif()
+
+ # Given foo.bar, the component name is bar
+ string(REPLACE "." ";" name_parts "${_tgt}")
+ # fail if the name doesn't look like foo.bar
+ list(LENGTH name_parts name_parts_length)
+ if(NOT name_parts_length EQUAL 2)
+ message(
+ FATAL_ERROR
+ "beman_install_library(${name}): expects a name of the form 'beman.', got '${_tgt}'"
+ )
+ endif()
+ list(GET name_parts -1 component_name)
+ set_target_properties(
+ "${_tgt}"
+ PROPERTIES EXPORT_NAME "${component_name}"
+ )
+ message(
+ VERBOSE
+ "beman_install_library(${name}): EXPORT_NAME ${component_name} for TARGET '${_tgt}'"
+ )
+
+ # Get the list of interface header sets, exact one expected!
+ set(_install_header_set_args)
+ get_target_property(
+ _available_header_sets
+ ${_tgt}
+ INTERFACE_HEADER_SETS
+ )
+ if(_available_header_sets)
+ message(
+ VERBOSE
+ "beman-install-library(${name}): '${_tgt}' has INTERFACE_HEADER_SETS=${_available_header_sets}"
+ )
+ foreach(_install_header_set IN LISTS _available_header_sets)
+ list(
+ APPEND
+ _install_header_set_args
+ FILE_SET
+ "${_install_header_set}"
+ COMPONENT
+ "${install_component_name}_Development"
+ )
+ endforeach()
+ else()
+ set(_install_header_set_args FILE_SET HEADERS) # Note: empty FILE_SET in this case! CK
+ endif()
+
+ # Detect presence of PUBLIC C++ module file sets. Note: exact one is expected!
+ get_target_property(_module_sets "${_tgt}" INTERFACE_CXX_MODULE_SETS)
+ if(_module_sets)
+ message(
+ VERBOSE
+ "beman-install-library(${name}): '${_tgt}' has INTERFACE_CXX_MODULE_SETS=${_module_sets}"
+ )
+ install(
+ TARGETS "${_tgt}"
+ EXPORT ${BEMAN_EXPORT_NAME}
+ ARCHIVE COMPONENT "${install_component_name}_Development"
+ LIBRARY
+ COMPONENT "${install_component_name}_Runtime"
+ NAMELINK_COMPONENT "${install_component_name}_Development"
+ RUNTIME COMPONENT "${install_component_name}_Runtime"
+ ${_install_header_set_args}
+ FILE_SET ${_module_sets}
+ DESTINATION "${BEMAN_DESTINATION}"
+ COMPONENT "${install_component_name}_Development"
+ # NOTE: There's currently no convention for this location! CK
+ CXX_MODULES_BMI
+ DESTINATION
+ ${_config_install_dir}/bmi-${CMAKE_CXX_COMPILER_ID}_$
+ COMPONENT "${install_component_name}_Development"
+ )
+ else()
+ install(
+ TARGETS "${_tgt}"
+ EXPORT ${BEMAN_EXPORT_NAME}
+ ARCHIVE COMPONENT "${install_component_name}_Development"
+ LIBRARY
+ COMPONENT "${install_component_name}_Runtime"
+ NAMELINK_COMPONENT "${install_component_name}_Development"
+ RUNTIME COMPONENT "${install_component_name}_Runtime"
+ ${_install_header_set_args}
+ )
+ endif()
+ endforeach()
+
+ # --------------------------------------------------
+ # Export targets
+ # --------------------------------------------------
+ # gersemi: off
+ install(
+ EXPORT ${BEMAN_EXPORT_NAME}
+ NAMESPACE ${BEMAN_NAMESPACE}
+ CXX_MODULES_DIRECTORY cxx-modules
+ DESTINATION ${_config_install_dir}
+ COMPONENT "${install_component_name}_Development"
+ )
+ # gersemi: on
+
+ # ----------------------------------------
+ # Config file installation logic
+ #
+ # Precedence (highest to lowest):
+ # 1. Per-package variable _INSTALL_CONFIG_FILE_PACKAGE
+ # 2. Allow-list BEMAN_INSTALL_CONFIG_FILE_PACKAGES (if defined)
+ # 3. Default: ON
+ # ----------------------------------------
+ string(TOUPPER "${name}" _pkg_upper)
+ string(REPLACE "." "_" _pkg_prefix "${_pkg_upper}")
+
+ option(
+ ${_pkg_prefix}_INSTALL_CONFIG_FILE_PACKAGE
+ "Enable creating and installing a CMake config-file package. Default: ON. Values: { ON, OFF }."
+ ON
+ )
+
+ set(_pkg_var "${_pkg_prefix}_INSTALL_CONFIG_FILE_PACKAGE")
+
+ # Default: install config files
+ set(_install_config ON)
+
+ # If the allow-list is defined, only install for packages in the list
+ if(DEFINED BEMAN_INSTALL_CONFIG_FILE_PACKAGES)
+ if(NOT "${name}" IN_LIST BEMAN_INSTALL_CONFIG_FILE_PACKAGES)
+ set(_install_config OFF)
+ endif()
+ endif()
+
+ # Per-package override takes highest precedence
+ if(DEFINED ${_pkg_var})
+ set(_install_config ${${_pkg_var}})
+ endif()
+
+ # ----------------------------------------
+ # expand dependencies
+ # ----------------------------------------
+ set(_beman_find_deps "")
+ foreach(dep IN LISTS BEMAN_DEPENDENCIES)
+ message(
+ VERBOSE
+ "beman-install-library(${name}): Add find_dependency(${dep})"
+ )
+ string(APPEND _beman_find_deps "find_dependency(${dep})\n")
+ endforeach()
+ set(BEMAN_FIND_DEPENDENCIES "${_beman_find_deps}")
+
+ # ----------------------------------------
+ # Generate + install config files
+ # ----------------------------------------
+ if(_install_config)
+ configure_package_config_file(
+ "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/Config.cmake.in"
+ "${CMAKE_CURRENT_BINARY_DIR}/${name}-config.cmake"
+ INSTALL_DESTINATION ${_config_install_dir}
+ )
+
+ write_basic_package_version_file(
+ "${CMAKE_CURRENT_BINARY_DIR}/${name}-config-version.cmake"
+ VERSION ${PROJECT_VERSION}
+ COMPATIBILITY SameMajorVersion
+ )
+
+ install(
+ FILES
+ "${CMAKE_CURRENT_BINARY_DIR}/${name}-config.cmake"
+ "${CMAKE_CURRENT_BINARY_DIR}/${name}-config-version.cmake"
+ DESTINATION ${_config_install_dir}
+ COMPONENT "${install_component_name}_Development"
+ )
+ else()
+ message(
+ WARNING
+ "beman-install-library(${name}): Not installing a config package for '${name}'"
+ )
+ endif()
+endfunction()
+
+set(CPACK_GENERATOR TGZ)
+include(CPack)
diff --git a/infra/cmake/use-fetch-content.cmake b/infra/cmake/use-fetch-content.cmake
index 4ed4839..eb22be4 100644
--- a/infra/cmake/use-fetch-content.cmake
+++ b/infra/cmake/use-fetch-content.cmake
@@ -170,6 +170,18 @@ function(BemanExemplar_provideDependency method package_name)
set(INSTALL_GTEST OFF) # Disable GoogleTest installation
FetchContent_MakeAvailable("${BemanExemplar_name}")
+ # Catch2's CTest integration module isn't on CMAKE_MODULE_PATH
+ # when brought in via FetchContent. Add it so that
+ # `include(Catch)` works.
+ if(BemanExemplar_pkgName STREQUAL "Catch2")
+ list(
+ APPEND
+ CMAKE_MODULE_PATH
+ "${${BemanExemplar_name}_SOURCE_DIR}/extras"
+ )
+ set(CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH}" PARENT_SCOPE)
+ endif()
+
# Important! _FOUND tells CMake that `find_package` is
# not needed for this package anymore
set("${BemanExemplar_pkgName}_FOUND" TRUE PARENT_SCOPE)
diff --git a/infra/tools/beman-submodule/README.md b/infra/tools/beman-submodule/README.md
deleted file mode 100644
index 36883ad..0000000
--- a/infra/tools/beman-submodule/README.md
+++ /dev/null
@@ -1,63 +0,0 @@
-# beman-submodule
-
-
-
-## What is this script?
-
-`beman-submodule` provides some of the features of `git submodule`, adding child git
-repositories to a parent git repository, but unlike with `git submodule`, the entire child
-repo is directly checked in, so only maintainers, not users, need to run this script. The
-command line interface mimics `git submodule`'s.
-
-## How do I add a beman submodule to my repository?
-
-The first beman submodule you should add is this repository, `infra/`, which you can
-bootstrap by running:
-
-
-```sh
-curl -s https://raw.githubusercontent.com/bemanproject/infra/refs/heads/main/tools/beman-submodule/beman-submodule | python3 - add https://github.com/bemanproject/infra.git
-```
-
-Once that's added, you can run the script from `infra/tools/beman-submodule/beman-submodule`.
-
-## How do I update a beman submodule to the latest trunk?
-
-You can run `beman-submodule update --remote` to update all beman submodule to latest
-trunk, or e.g. `beman-submodule update --remote infra` to update only a specific one.
-
-## How does it work under the hood?
-
-Along with the files from the child repository, it creates a dotfile called
-`.beman_submodule`, which looks like this:
-
-```ini
-[beman_submodule]
-remote=https://github.com/bemanproject/infra.git
-commit_hash=9b88395a86c4290794e503e94d8213b6c442ae77
-```
-
-## How do I update a beman submodule to a specific commit or change the remote URL?
-
-You can edit the corresponding lines in the `.beman_submodule` file and run
-`beman-submodule update` to update the state of the beman submodule to the new
-`.beman_submodule` settings.
-
-## How can I make CI ensure that my beman submodules are in a valid state?
-
-Add this job to your CI workflow:
-
-```yaml
- beman-submodule-test:
- runs-on: ubuntu-latest
- name: "Check beman submodules for consistency"
- steps:
- - name: Checkout
- uses: actions/checkout@v4
- - name: beman submodule consistency check
- run: |
- (set -o pipefail; ./infra/tools/beman-submodule/beman-submodule status | grep -qvF '+')
-```
-
-This will fail if the contents of any beman submodule don't match what's specified in the
-`.beman_submodule` file.
diff --git a/infra/tools/beman-submodule/beman-submodule b/infra/tools/beman-submodule/beman-submodule
deleted file mode 100755
index 66cb96e..0000000
--- a/infra/tools/beman-submodule/beman-submodule
+++ /dev/null
@@ -1,260 +0,0 @@
-#!/usr/bin/env python3
-
-# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-
-import argparse
-import configparser
-import filecmp
-import glob
-import os
-import shutil
-import subprocess
-import sys
-import tempfile
-from pathlib import Path
-
-
-def directory_compare(
- reference: str | Path, actual: str | Path, ignore, allow_untracked_files: bool):
- reference, actual = Path(reference), Path(actual)
-
- compared = filecmp.dircmp(reference, actual, ignore=ignore)
- if (compared.left_only
- or (compared.right_only and not allow_untracked_files)
- or compared.diff_files):
- return False
- for common_dir in compared.common_dirs:
- path1 = reference / common_dir
- path2 = actual / common_dir
- if not directory_compare(path1, path2, ignore, allow_untracked_files):
- return False
- return True
-
-class BemanSubmodule:
- def __init__(
- self, dirpath: str | Path, remote: str, commit_hash: str,
- allow_untracked_files: bool):
- self.dirpath = Path(dirpath)
- self.remote = remote
- self.commit_hash = commit_hash
- self.allow_untracked_files = allow_untracked_files
-
-def parse_beman_submodule_file(path):
- config = configparser.ConfigParser()
- read_result = config.read(path)
- def fail():
- raise Exception(f'Failed to parse {path} as a .beman_submodule file')
- if not read_result:
- fail()
- if not 'beman_submodule' in config:
- fail()
- if not 'remote' in config['beman_submodule']:
- fail()
- if not 'commit_hash' in config['beman_submodule']:
- fail()
- allow_untracked_files = config.getboolean(
- 'beman_submodule', 'allow_untracked_files', fallback=False)
- return BemanSubmodule(
- Path(path).resolve().parent,
- config['beman_submodule']['remote'],
- config['beman_submodule']['commit_hash'],
- allow_untracked_files)
-
-def get_beman_submodule(path: str | Path):
- beman_submodule_filepath = Path(path) / '.beman_submodule'
-
- if beman_submodule_filepath.is_file():
- return parse_beman_submodule_file(beman_submodule_filepath)
- else:
- return None
-
-def find_beman_submodules_in(path):
- path = Path(path)
- assert path.is_dir()
-
- result = []
- for dirpath, _, filenames in path.walk():
- if '.beman_submodule' in filenames:
- result.append(parse_beman_submodule_file(dirpath / '.beman_submodule'))
- return sorted(result, key=lambda module: module.dirpath)
-
-def cwd_git_repository_path():
- process = subprocess.run(
- ['git', 'rev-parse', '--show-toplevel'], capture_output=True, text=True,
- check=False)
- if process.returncode == 0:
- return process.stdout.strip()
- elif "fatal: not a git repository" in process.stderr:
- return None
- else:
- raise Exception("git rev-parse --show-toplevel failed")
-
-def clone_beman_submodule_into_tmpdir(beman_submodule, remote):
- tmpdir = tempfile.TemporaryDirectory()
- subprocess.run(
- ['git', 'clone', beman_submodule.remote, tmpdir.name], capture_output=True,
- check=True)
- if not remote:
- subprocess.run(
- ['git', '-C', tmpdir.name, 'reset', '--hard', beman_submodule.commit_hash],
- capture_output=True, check=True)
- return tmpdir
-
-def get_paths(beman_submodule):
- tmpdir = clone_beman_submodule_into_tmpdir(beman_submodule, False)
- paths = set(glob.glob('*', root_dir=Path(tmpdir.name), include_hidden=True))
- paths.remove('.git')
- return paths
-
-def beman_submodule_status(beman_submodule):
- tmpdir = clone_beman_submodule_into_tmpdir(beman_submodule, False)
- if directory_compare(
- tmpdir.name, beman_submodule.dirpath, ['.beman_submodule', '.git'],
- beman_submodule.allow_untracked_files):
- status_character=' '
- else:
- status_character='+'
- parent_repo_path = cwd_git_repository_path()
- if not parent_repo_path:
- raise Exception('this is not a git repository')
- relpath = Path(beman_submodule.dirpath).relative_to(Path(parent_repo_path))
- return status_character + ' ' + beman_submodule.commit_hash + ' ' + str(relpath)
-
-def beman_submodule_update(beman_submodule, remote):
- tmpdir = clone_beman_submodule_into_tmpdir(beman_submodule, remote)
- tmp_path = Path(tmpdir.name)
- sha_process = subprocess.run(
- ['git', 'rev-parse', 'HEAD'], capture_output=True, check=True, text=True,
- cwd=tmp_path)
-
- if beman_submodule.allow_untracked_files:
- for path in get_paths(beman_submodule):
- path2 = Path(beman_submodule.dirpath) / path
- if Path(path2).is_dir():
- shutil.rmtree(path2)
- elif Path(path2).is_file():
- os.remove(path2)
- else:
- shutil.rmtree(beman_submodule.dirpath)
-
- submodule_path = tmp_path / '.beman_submodule'
- with open(submodule_path, 'w') as f:
- f.write('[beman_submodule]\n')
- f.write(f'remote={beman_submodule.remote}\n')
- f.write(f'commit_hash={sha_process.stdout.strip()}\n')
- if beman_submodule.allow_untracked_files:
- f.write(f'allow_untracked_files=True\n')
- shutil.rmtree(tmp_path / '.git')
- shutil.copytree(tmp_path, beman_submodule.dirpath, dirs_exist_ok=True)
-
-def update_command(remote, path):
- if not path:
- parent_repo_path = cwd_git_repository_path()
- if not parent_repo_path:
- raise Exception('this is not a git repository')
- beman_submodules = find_beman_submodules_in(parent_repo_path)
- else:
- beman_submodule = get_beman_submodule(path)
- if not beman_submodule:
- raise Exception(f'{path} is not a beman_submodule')
- beman_submodules = [beman_submodule]
- for beman_submodule in beman_submodules:
- beman_submodule_update(beman_submodule, remote)
-
-def add_command(repository, path, allow_untracked_files):
- tmpdir = tempfile.TemporaryDirectory()
- subprocess.run(
- ['git', 'clone', repository], capture_output=True, check=True, cwd=tmpdir.name)
- repository_name = os.listdir(tmpdir.name)[0]
- if not path:
- path = Path(repository_name)
- else:
- path = Path(path)
- if not allow_untracked_files and path.exists():
- raise Exception(f'{path} exists')
- path.mkdir(exist_ok=allow_untracked_files)
- tmpdir_repo = Path(tmpdir.name) / repository_name
- sha_process = subprocess.run(
- ['git', 'rev-parse', 'HEAD'], capture_output=True, check=True, text=True,
- cwd=tmpdir_repo)
- with open(tmpdir_repo / '.beman_submodule', 'w') as f:
- f.write('[beman_submodule]\n')
- f.write(f'remote={repository}\n')
- f.write(f'commit_hash={sha_process.stdout.strip()}\n')
- if allow_untracked_files:
- f.write(f'allow_untracked_files=True\n')
- shutil.rmtree(tmpdir_repo /'.git')
- shutil.copytree(tmpdir_repo, path, dirs_exist_ok=True)
-
-def status_command(paths):
- if not paths:
- parent_repo_path = cwd_git_repository_path()
- if not parent_repo_path:
- raise Exception('this is not a git repository')
- beman_submodules = find_beman_submodules_in(parent_repo_path)
- else:
- beman_submodules = []
- for path in paths:
- beman_submodule = get_beman_submodule(path)
- if not beman_submodule:
- raise Exception(f'{path} is not a beman_submodule')
- beman_submodules.append(beman_submodule)
- for beman_submodule in beman_submodules:
- print(beman_submodule_status(beman_submodule))
-
-def get_parser():
- parser = argparse.ArgumentParser(description='Beman pseudo-submodule tool')
- subparsers = parser.add_subparsers(dest='command', help='available commands')
- parser_update = subparsers.add_parser('update', help='update beman_submodules')
- parser_update.add_argument(
- '--remote', action='store_true',
- help='update a beman_submodule to its latest from upstream')
- parser_update.add_argument(
- 'beman_submodule_path', nargs='?',
- help='relative path to the beman_submodule to update')
- parser_add = subparsers.add_parser('add', help='add a new beman_submodule')
- parser_add.add_argument('repository', help='git repository to add')
- parser_add.add_argument(
- 'path', nargs='?', help='path where the repository will be added')
- parser_add.add_argument(
- '--allow-untracked-files', action='store_true',
- help='the beman_submodule will not occupy the subdirectory exclusively')
- parser_status = subparsers.add_parser(
- 'status', help='show the status of beman_submodules')
- parser_status.add_argument('paths', nargs='*')
- return parser
-
-def parse_args(args):
- return get_parser().parse_args(args);
-
-def usage():
- return get_parser().format_help()
-
-def run_command(args):
- if args.command == 'update':
- update_command(args.remote, args.beman_submodule_path)
- elif args.command == 'add':
- add_command(args.repository, args.path, args.allow_untracked_files)
- elif args.command == 'status':
- status_command(args.paths)
- else:
- raise Exception(usage())
-
-def check_for_git(path):
- env = os.environ.copy()
- if path is not None:
- env["PATH"] = path
- return shutil.which("git", path=env.get("PATH")) is not None
-
-def main():
- try:
- if not check_for_git(None):
- raise Exception('git not found in PATH')
- args = parse_args(sys.argv[1:])
- run_command(args)
- except Exception as e:
- print("Error:", e, file=sys.stderr)
- sys.exit(1)
-
-if __name__ == '__main__':
- main()
diff --git a/infra/tools/beman-submodule/test/test_beman_submodule.py b/infra/tools/beman-submodule/test/test_beman_submodule.py
deleted file mode 100644
index 600fc07..0000000
--- a/infra/tools/beman-submodule/test/test_beman_submodule.py
+++ /dev/null
@@ -1,539 +0,0 @@
-# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-
-import glob
-import os
-import pytest
-import shutil
-import stat
-import subprocess
-import tempfile
-from pathlib import Path
-
-# https://stackoverflow.com/a/19011259
-import types
-import importlib.machinery
-loader = importlib.machinery.SourceFileLoader(
- 'beman_submodule',
- str(Path(__file__).parent.resolve().parent / 'beman-submodule'))
-beman_submodule = types.ModuleType(loader.name)
-loader.exec_module(beman_submodule)
-
-def create_test_git_repository():
- tmpdir = tempfile.TemporaryDirectory()
- tmp_path = Path(tmpdir.name)
-
- subprocess.run(['git', 'init'], check=True, cwd=tmpdir.name, capture_output=True)
- def make_commit(a_txt_contents):
- with open(tmp_path / 'a.txt', 'w') as f:
- f.write(a_txt_contents)
- subprocess.run(
- ['git', 'add', 'a.txt'], check=True, cwd=tmpdir.name, capture_output=True)
- subprocess.run(
- ['git', '-c', 'user.name=test', '-c', 'user.email=test@example.com', 'commit',
- '--author="test "', '-m', 'test'],
- check=True, cwd=tmpdir.name, capture_output=True)
- make_commit('A')
- make_commit('a')
- return tmpdir
-
-def create_test_git_repository2():
- tmpdir = tempfile.TemporaryDirectory()
- tmp_path = Path(tmpdir.name)
-
- subprocess.run(['git', 'init'], check=True, cwd=tmpdir.name, capture_output=True)
- with open(tmp_path / 'a.txt', 'w') as f:
- f.write('a')
- subprocess.run(
- ['git', 'add', 'a.txt'], check=True, cwd=tmpdir.name, capture_output=True)
- subprocess.run(
- ['git', '-c', 'user.name=test', '-c', 'user.email=test@example.com', 'commit',
- '--author="test "', '-m', 'test'],
- check=True, cwd=tmpdir.name, capture_output=True)
- os.remove(tmp_path / 'a.txt')
- subprocess.run(
- ['git', 'rm', 'a.txt'], check=True, cwd=tmpdir.name, capture_output=True)
- with open(tmp_path / 'b.txt', 'w') as f:
- f.write('b')
- subprocess.run(
- ['git', 'add', 'b.txt'], check=True, cwd=tmpdir.name, capture_output=True)
- subprocess.run(
- ['git', '-c', 'user.name=test', '-c', 'user.email=test@example.com', 'commit',
- '--author="test "', '-m', 'test'],
- check=True, cwd=tmpdir.name, capture_output=True)
- return tmpdir
-
-def test_directory_compare():
- def create_dir_structure(dir_path: Path):
- bar_path = dir_path / 'bar'
- os.makedirs(bar_path)
-
- with open(dir_path / 'foo.txt', 'w') as f:
- f.write('foo')
- with open(bar_path / 'baz.txt', 'w') as f:
- f.write('baz')
-
- with tempfile.TemporaryDirectory() as dir_a, \
- tempfile.TemporaryDirectory() as dir_b:
- path_a = Path(dir_a)
- path_b = Path(dir_b)
-
- create_dir_structure(path_a)
- create_dir_structure(path_b)
-
- assert beman_submodule.directory_compare(dir_a, dir_b, [], False)
-
- with open(path_a / 'bar' / 'quux.txt', 'w') as f:
- f.write('quux')
-
- assert not beman_submodule.directory_compare(path_a, path_b, [], False)
- assert beman_submodule.directory_compare(path_a, path_b, ['quux.txt'], False)
-
-def test_directory_compare_untracked_files():
- def create_dir_structure(dir_path: Path):
- bar_path = dir_path / 'bar'
- os.makedirs(bar_path)
-
- with open(dir_path / 'foo.txt', 'w') as f:
- f.write('foo')
- with open(bar_path / 'baz.txt', 'w') as f:
- f.write('baz')
-
- with tempfile.TemporaryDirectory() as reference, \
- tempfile.TemporaryDirectory() as actual:
- path_a = Path(reference)
- path_b = Path(actual)
-
- create_dir_structure(path_a)
- create_dir_structure(path_b)
- (path_b / 'c.txt').touch()
-
- assert beman_submodule.directory_compare(reference, actual, [], True)
-
- with open(path_a / 'bar' / 'quux.txt', 'w') as f:
- f.write('quux')
-
- assert not beman_submodule.directory_compare(path_a, path_b, [], True)
- assert beman_submodule.directory_compare(path_a, path_b, ['quux.txt'], True)
-
-def test_parse_beman_submodule_file():
- def valid_file():
- tmpfile = tempfile.NamedTemporaryFile()
- tmpfile.write('[beman_submodule]\n'.encode('utf-8'))
- tmpfile.write(
- 'remote=git@github.com:bemanproject/infra.git\n'.encode('utf-8'))
- tmpfile.write(
- 'commit_hash=9b88395a86c4290794e503e94d8213b6c442ae77\n'.encode('utf-8'))
- tmpfile.flush()
- module = beman_submodule.parse_beman_submodule_file(tmpfile.name)
- assert module.dirpath == Path(tmpfile.name).resolve().parent
- assert module.remote == 'git@github.com:bemanproject/infra.git'
- assert module.commit_hash == '9b88395a86c4290794e503e94d8213b6c442ae77'
- valid_file()
- def invalid_file_missing_remote():
- threw = False
- try:
- tmpfile = tempfile.NamedTemporaryFile()
- tmpfile.write('[beman_submodule]\n'.encode('utf-8'))
- tmpfile.write(
- 'commit_hash=9b88395a86c4290794e503e94d8213b6c442ae77\n'.encode('utf-8'))
- tmpfile.flush()
- beman_submodule.parse_beman_submodule_file(tmpfile.name)
- except:
- threw = True
- assert threw
- invalid_file_missing_remote()
- def invalid_file_missing_commit_hash():
- threw = False
- try:
- tmpfile = tempfile.NamedTemporaryFile()
- tmpfile.write('[beman_submodule]\n'.encode('utf-8'))
- tmpfile.write(
- 'remote=git@github.com:bemanproject/infra.git\n'.encode('utf-8'))
- tmpfile.flush()
- beman_submodule.parse_beman_submodule_file(tmpfile.name)
- except:
- threw = True
- assert threw
- invalid_file_missing_commit_hash()
- def invalid_file_wrong_section():
- threw = False
- try:
- tmpfile = tempfile.NamedTemporaryFile()
- tmpfile.write('[invalid]\n'.encode('utf-8'))
- tmpfile.write(
- 'remote=git@github.com:bemanproject/infra.git\n'.encode('utf-8'))
- tmpfile.write(
- 'commit_hash=9b88395a86c4290794e503e94d8213b6c442ae77\n'.encode('utf-8'))
- tmpfile.flush()
- beman_submodule.parse_beman_submodule_file(tmpfile.name)
- except:
- threw = True
- assert threw
- invalid_file_wrong_section()
-
-def test_get_beman_submodule():
- tmpdir = create_test_git_repository()
- tmpdir2 = create_test_git_repository()
- original_cwd = Path.cwd()
- os.chdir(tmpdir2.name)
- beman_submodule.add_command(tmpdir.name, 'foo', False)
- assert beman_submodule.get_beman_submodule('foo')
- os.remove('foo/.beman_submodule')
- assert not beman_submodule.get_beman_submodule('foo')
- os.chdir(original_cwd)
-
-def test_find_beman_submodules_in():
- tmpdir = create_test_git_repository()
- tmpdir2 = create_test_git_repository()
- original_cwd = Path.cwd()
- os.chdir(tmpdir2.name)
- beman_submodule.add_command(tmpdir.name, 'foo', False)
- beman_submodule.add_command(tmpdir.name, 'bar', False)
- beman_submodules = beman_submodule.find_beman_submodules_in(tmpdir2.name)
- sha_process = subprocess.run(
- ['git', 'rev-parse', 'HEAD'], capture_output=True, check=True, text=True,
- cwd=tmpdir.name)
- sha = sha_process.stdout.strip()
- assert beman_submodules[0].dirpath == Path(tmpdir2.name) / 'bar'
- assert beman_submodules[0].remote == tmpdir.name
- assert beman_submodules[0].commit_hash == sha
- assert beman_submodules[1].dirpath == Path(tmpdir2.name) / 'foo'
- assert beman_submodules[1].remote == tmpdir.name
- assert beman_submodules[1].commit_hash == sha
- os.chdir(original_cwd)
-
-def test_cwd_git_repository_path():
- original_cwd = Path.cwd()
- tmpdir = tempfile.TemporaryDirectory()
- os.chdir(tmpdir.name)
- assert not beman_submodule.cwd_git_repository_path()
- subprocess.run(['git', 'init'])
- assert beman_submodule.cwd_git_repository_path() == tmpdir.name
- os.chdir(original_cwd)
-
-def test_clone_beman_submodule_into_tmpdir():
- tmpdir = create_test_git_repository()
- tmpdir2 = create_test_git_repository()
- original_cwd = Path.cwd()
- os.chdir(tmpdir2.name)
- sha_process = subprocess.run(
- ['git', 'rev-parse', 'HEAD^'], capture_output=True, check=True, text=True,
- cwd=tmpdir.name)
- sha = sha_process.stdout.strip()
- beman_submodule.add_command(tmpdir.name, 'foo', False)
- module = beman_submodule.get_beman_submodule(Path(tmpdir2.name) / 'foo')
- module.commit_hash = sha
- tmpdir3 = beman_submodule.clone_beman_submodule_into_tmpdir(module, False)
- assert not beman_submodule.directory_compare(
- tmpdir.name, tmpdir3.name, ['.git'], False)
- tmpdir4 = beman_submodule.clone_beman_submodule_into_tmpdir(module, True)
- assert beman_submodule.directory_compare(tmpdir.name, tmpdir4.name, ['.git'], False)
- subprocess.run(
- ['git', 'reset', '--hard', sha], capture_output=True, check=True,
- cwd=tmpdir.name)
- assert beman_submodule.directory_compare(tmpdir.name, tmpdir3.name, ['.git'], False)
- os.chdir(original_cwd)
-
-def test_get_paths():
- tmpdir = create_test_git_repository()
- tmpdir2 = create_test_git_repository()
- original_cwd = Path.cwd()
- os.chdir(tmpdir2.name)
- beman_submodule.add_command(tmpdir.name, 'foo', False)
- module = beman_submodule.get_beman_submodule(Path(tmpdir2.name) / 'foo')
- assert beman_submodule.get_paths(module) == set(['a.txt'])
- os.chdir(original_cwd)
-
-def test_beman_submodule_status():
- tmpdir = create_test_git_repository()
- tmpdir2 = create_test_git_repository()
- original_cwd = Path.cwd()
- os.chdir(tmpdir2.name)
- beman_submodule.add_command(tmpdir.name, 'foo', False)
- sha_process = subprocess.run(
- ['git', 'rev-parse', 'HEAD'], capture_output=True, check=True, text=True,
- cwd=tmpdir.name)
- sha = sha_process.stdout.strip()
- assert ' ' + sha + ' foo' == beman_submodule.beman_submodule_status(
- beman_submodule.get_beman_submodule(Path(tmpdir2.name) / 'foo'))
- with open(Path(tmpdir2.name) / 'foo' / 'a.txt', 'w') as f:
- f.write('b')
- assert '+ ' + sha + ' foo' == beman_submodule.beman_submodule_status(
- beman_submodule.get_beman_submodule(Path(tmpdir2.name) / 'foo'))
- os.chdir(original_cwd)
-
-def test_update_command_no_paths():
- tmpdir = create_test_git_repository()
- tmpdir2 = create_test_git_repository()
- original_cwd = Path.cwd()
- os.chdir(tmpdir2.name)
- orig_sha_process = subprocess.run(
- ['git', 'rev-parse', 'HEAD'], capture_output=True, check=True, text=True,
- cwd=tmpdir.name)
- orig_sha = orig_sha_process.stdout.strip()
- parent_sha_process = subprocess.run(
- ['git', 'rev-parse', 'HEAD^'], capture_output=True, check=True, text=True,
- cwd=tmpdir.name)
- parent_sha = parent_sha_process.stdout.strip()
- parent_parent_sha_process = subprocess.run(
- ['git', 'rev-parse', 'HEAD^'], capture_output=True, check=True, text=True,
- cwd=tmpdir.name)
- parent_parent_sha = parent_parent_sha_process.stdout.strip()
- subprocess.run(
- ['git', 'reset', '--hard', parent_parent_sha], capture_output=True, check=True,
- cwd=tmpdir.name)
- beman_submodule.add_command(tmpdir.name, 'foo', False)
- beman_submodule.add_command(tmpdir.name, 'bar', False)
- subprocess.run(
- ['git', 'reset', '--hard', orig_sha], capture_output=True, check=True,
- cwd=tmpdir.name)
- with open(Path(tmpdir2.name) / 'foo' / '.beman_submodule', 'w') as f:
- f.write(f'[beman_submodule]\nremote={tmpdir.name}\ncommit_hash={parent_sha}\n')
- with open(Path(tmpdir2.name) / 'bar' / '.beman_submodule', 'w') as f:
- f.write(f'[beman_submodule]\nremote={tmpdir.name}\ncommit_hash={parent_sha}\n')
- beman_submodule.update_command(False, None)
- with open(Path(tmpdir2.name) / 'foo' / '.beman_submodule', 'r') as f:
- assert f.read() == f'[beman_submodule]\nremote={tmpdir.name}\ncommit_hash={parent_sha}\n'
- with open(Path(tmpdir2.name) / 'bar' / '.beman_submodule', 'r') as f:
- assert f.read() == f'[beman_submodule]\nremote={tmpdir.name}\ncommit_hash={parent_sha}\n'
- subprocess.run(
- ['git', 'reset', '--hard', parent_sha], capture_output=True, check=True,
- cwd=tmpdir.name)
- assert beman_submodule.directory_compare(
- tmpdir.name, Path(tmpdir2.name) / 'foo', ['.git', '.beman_submodule'], False)
- assert beman_submodule.directory_compare(
- tmpdir.name, Path(tmpdir2.name) / 'bar', ['.git', '.beman_submodule'], False)
- subprocess.run(
- ['git', 'reset', '--hard', orig_sha], capture_output=True, check=True,
- cwd=tmpdir.name)
- beman_submodule.update_command(True, None)
- with open(Path(tmpdir2.name) / 'foo' / '.beman_submodule', 'r') as f:
- assert f.read() == f'[beman_submodule]\nremote={tmpdir.name}\ncommit_hash={orig_sha}\n'
- with open(Path(tmpdir2.name) / 'bar' / '.beman_submodule', 'r') as f:
- assert f.read() == f'[beman_submodule]\nremote={tmpdir.name}\ncommit_hash={orig_sha}\n'
- assert beman_submodule.directory_compare(
- tmpdir.name, Path(tmpdir2.name) / 'foo', ['.git', '.beman_submodule'], False)
- assert beman_submodule.directory_compare(
- tmpdir.name, Path(tmpdir2.name) / 'bar', ['.git', '.beman_submodule'], False)
- os.chdir(original_cwd)
-
-def test_update_command_with_path():
- tmpdir = create_test_git_repository()
- tmpdir2 = create_test_git_repository()
- original_cwd = Path.cwd()
- os.chdir(tmpdir2.name)
- orig_sha_process = subprocess.run(
- ['git', 'rev-parse', 'HEAD'], capture_output=True, check=True, text=True,
- cwd=tmpdir.name)
- orig_sha = orig_sha_process.stdout.strip()
- parent_sha_process = subprocess.run(
- ['git', 'rev-parse', 'HEAD^'], capture_output=True, check=True, text=True,
- cwd=tmpdir.name)
- parent_sha = parent_sha_process.stdout.strip()
- parent_parent_sha_process = subprocess.run(
- ['git', 'rev-parse', 'HEAD^'], capture_output=True, check=True, text=True,
- cwd=tmpdir.name)
- parent_parent_sha = parent_parent_sha_process.stdout.strip()
- subprocess.run(
- ['git', 'reset', '--hard', parent_parent_sha], capture_output=True, check=True,
- cwd=tmpdir.name)
- tmpdir_parent_parent_copy = tempfile.TemporaryDirectory()
- shutil.copytree(tmpdir.name, tmpdir_parent_parent_copy.name, dirs_exist_ok=True)
- beman_submodule.add_command(tmpdir.name, 'foo', False)
- beman_submodule.add_command(tmpdir.name, 'bar', False)
- subprocess.run(
- ['git', 'reset', '--hard', orig_sha], capture_output=True, check=True,
- cwd=tmpdir.name)
- with open(Path(tmpdir2.name) / 'foo' / '.beman_submodule', 'w') as f:
- f.write(f'[beman_submodule]\nremote={tmpdir.name}\ncommit_hash={parent_sha}\n')
- with open(Path(tmpdir2.name) / 'bar' / '.beman_submodule', 'w') as f:
- f.write(f'[beman_submodule]\nremote={tmpdir.name}\ncommit_hash={parent_sha}\n')
- beman_submodule.update_command(False, 'foo')
- with open(Path(tmpdir2.name) / 'foo' / '.beman_submodule', 'r') as f:
- assert f.read() == f'[beman_submodule]\nremote={tmpdir.name}\ncommit_hash={parent_sha}\n'
- with open(Path(tmpdir2.name) / 'bar' / '.beman_submodule', 'r') as f:
- assert f.read() == f'[beman_submodule]\nremote={tmpdir.name}\ncommit_hash={parent_sha}\n'
- subprocess.run(
- ['git', 'reset', '--hard', parent_sha], capture_output=True, check=True,
- cwd=tmpdir.name)
- assert beman_submodule.directory_compare(
- tmpdir.name, Path(tmpdir2.name) / 'foo', ['.git', '.beman_submodule'], False)
- assert beman_submodule.directory_compare(
- tmpdir_parent_parent_copy.name,
- Path(tmpdir2.name) / 'bar', ['.git', '.beman_submodule'], False)
- subprocess.run(
- ['git', 'reset', '--hard', orig_sha], capture_output=True, check=True,
- cwd=tmpdir.name)
- beman_submodule.update_command(True, 'foo')
- with open(Path(tmpdir2.name) / 'foo' / '.beman_submodule', 'r') as f:
- assert f.read() == f'[beman_submodule]\nremote={tmpdir.name}\ncommit_hash={orig_sha}\n'
- with open(Path(tmpdir2.name) / 'bar' / '.beman_submodule', 'r') as f:
- assert f.read() == f'[beman_submodule]\nremote={tmpdir.name}\ncommit_hash={parent_sha}\n'
- assert beman_submodule.directory_compare(
- tmpdir.name, Path(tmpdir2.name) / 'foo', ['.git', '.beman_submodule'], False)
- assert beman_submodule.directory_compare(
- tmpdir_parent_parent_copy.name,
- Path(tmpdir2.name) / 'bar', ['.git', '.beman_submodule'], False)
- os.chdir(original_cwd)
-
-def test_update_command_untracked_files():
- tmpdir = create_test_git_repository2()
- tmpdir2 = create_test_git_repository()
- original_cwd = Path.cwd();
- os.chdir(tmpdir2.name)
- orig_sha_process = subprocess.run(
- ['git', 'rev-parse', 'HEAD'], capture_output=True, check=True, text=True,
- cwd=tmpdir.name)
- orig_sha = orig_sha_process.stdout.strip()
- parent_sha_process = subprocess.run(
- ['git', 'rev-parse', 'HEAD^'], capture_output=True, check=True, text=True,
- cwd=tmpdir.name)
- parent_sha = parent_sha_process.stdout.strip()
- os.makedirs(Path(tmpdir2.name) / 'foo')
- (Path(tmpdir2.name) / 'foo' / 'c.txt').touch()
- with open(Path(tmpdir2.name) / 'foo' / '.beman_submodule', 'w') as f:
- f.write(f'[beman_submodule]\nremote={tmpdir.name}\ncommit_hash={parent_sha}\nallow_untracked_files=True')
- beman_submodule.update_command(False, 'foo')
- assert set(['./foo/a.txt', './foo/c.txt']) == set(glob.glob('./foo/*.txt'))
- beman_submodule.update_command(True, 'foo')
- assert set(['./foo/b.txt', './foo/c.txt']) == set(glob.glob('./foo/*.txt'))
- os.chdir(original_cwd)
-
-def test_add_command():
- tmpdir = create_test_git_repository()
- tmpdir2 = create_test_git_repository()
- original_cwd = Path.cwd()
- os.chdir(tmpdir2.name)
- beman_submodule.add_command(tmpdir.name, 'foo', False)
- sha_process = subprocess.run(
- ['git', 'rev-parse', 'HEAD'], capture_output=True, check=True, text=True,
- cwd=tmpdir.name)
- sha = sha_process.stdout.strip()
- assert beman_submodule.directory_compare(
- tmpdir.name, Path(tmpdir2.name) / 'foo', ['.git', '.beman_submodule'], False)
- with open(Path(tmpdir2.name) / 'foo' / '.beman_submodule', 'r') as f:
- assert f.read() == f'[beman_submodule]\nremote={tmpdir.name}\ncommit_hash={sha}\n'
- os.chdir(original_cwd)
-
-def test_add_command_untracked_files():
- tmpdir = create_test_git_repository()
- tmpdir2 = create_test_git_repository()
- original_cwd = Path.cwd()
- os.chdir(tmpdir2.name)
- os.makedirs(Path(tmpdir2.name) / 'foo')
- (Path(tmpdir2.name) / 'foo' / 'c.txt').touch()
- beman_submodule.add_command(tmpdir.name, 'foo', True)
- assert set(['./foo/a.txt', './foo/c.txt']) == set(glob.glob('./foo/*.txt'))
- os.chdir(original_cwd)
-
-def test_status_command_no_paths(capsys):
- tmpdir = create_test_git_repository()
- tmpdir2 = create_test_git_repository()
- original_cwd = Path.cwd()
- os.chdir(tmpdir2.name)
- beman_submodule.add_command(tmpdir.name, 'foo', False)
- beman_submodule.add_command(tmpdir.name, 'bar', False)
- sha_process = subprocess.run(
- ['git', 'rev-parse', 'HEAD'], capture_output=True, check=True, text=True,
- cwd=tmpdir.name)
- with open(Path(tmpdir2.name) / 'bar' / 'a.txt', 'w') as f:
- f.write('b')
- beman_submodule.status_command([])
- sha = sha_process.stdout.strip()
- assert capsys.readouterr().out == '+ ' + sha + ' bar\n' + ' ' + sha + ' foo\n'
- os.chdir(original_cwd)
-
-def test_status_command_with_path(capsys):
- tmpdir = create_test_git_repository()
- tmpdir2 = create_test_git_repository()
- original_cwd = Path.cwd()
- os.chdir(tmpdir2.name)
- beman_submodule.add_command(tmpdir.name, 'foo', False)
- beman_submodule.add_command(tmpdir.name, 'bar', False)
- sha_process = subprocess.run(
- ['git', 'rev-parse', 'HEAD'], capture_output=True, check=True, text=True,
- cwd=tmpdir.name)
- with open(Path(tmpdir2.name) / 'bar' / 'a.txt', 'w') as f:
- f.write('b')
- beman_submodule.status_command(['bar'])
- sha = sha_process.stdout.strip()
- assert capsys.readouterr().out == '+ ' + sha + ' bar\n'
- os.chdir(original_cwd)
-
-def test_status_command_untracked_files(capsys):
- tmpdir = create_test_git_repository()
- tmpdir2 = create_test_git_repository()
- original_cwd = Path.cwd()
- os.chdir(tmpdir2.name)
- beman_submodule.add_command(tmpdir.name, 'foo', True)
- sha_process = subprocess.run(
- ['git', 'rev-parse', 'HEAD'], capture_output=True, check=True, text=True,
- cwd=tmpdir.name)
- (Path(tmpdir2.name) / 'foo' / 'c.txt').touch()
- beman_submodule.status_command(['foo'])
- sha = sha_process.stdout.strip()
- assert capsys.readouterr().out == ' ' + sha + ' foo\n'
- os.chdir(original_cwd)
-
-def test_check_for_git():
- tmpdir = tempfile.TemporaryDirectory()
- assert not beman_submodule.check_for_git(tmpdir.name)
- fake_git_path = Path(tmpdir.name) / 'git'
- with open(fake_git_path, 'w'):
- pass
- os.chmod(fake_git_path, stat.S_IRWXU)
- assert beman_submodule.check_for_git(tmpdir.name)
-
-def test_parse_args():
- def plain_update():
- args = beman_submodule.parse_args(['update'])
- assert args.command == 'update'
- assert not args.remote
- assert not args.beman_submodule_path
- plain_update()
- def update_remote():
- args = beman_submodule.parse_args(['update', '--remote'])
- assert args.command == 'update'
- assert args.remote
- assert not args.beman_submodule_path
- update_remote()
- def update_path():
- args = beman_submodule.parse_args(['update', 'infra/'])
- assert args.command == 'update'
- assert not args.remote
- assert args.beman_submodule_path == 'infra/'
- update_path()
- def update_path_remote():
- args = beman_submodule.parse_args(['update', '--remote', 'infra/'])
- assert args.command == 'update'
- assert args.remote
- assert args.beman_submodule_path == 'infra/'
- update_path_remote()
- def plain_add():
- args = beman_submodule.parse_args(['add', 'git@github.com:bemanproject/infra.git'])
- assert args.command == 'add'
- assert args.repository == 'git@github.com:bemanproject/infra.git'
- assert not args.path
- plain_add()
- def add_path():
- args = beman_submodule.parse_args(
- ['add', 'git@github.com:bemanproject/infra.git', 'infra/'])
- assert args.command == 'add'
- assert args.repository == 'git@github.com:bemanproject/infra.git'
- assert args.path == 'infra/'
- add_path()
- def plain_status():
- args = beman_submodule.parse_args(['status'])
- assert args.command == 'status'
- assert args.paths == []
- plain_status()
- def status_one_module():
- args = beman_submodule.parse_args(['status', 'infra/'])
- assert args.command == 'status'
- assert args.paths == ['infra/']
- status_one_module()
- def status_multiple_modules():
- args = beman_submodule.parse_args(['status', 'infra/', 'foobar/'])
- assert args.command == 'status'
- assert args.paths == ['infra/', 'foobar/']
- status_multiple_modules()
diff --git a/src/beman/timed_lock_alg/CMakeLists.txt b/src/beman/timed_lock_alg/CMakeLists.txt
index 6e32603..77adb0c 100644
--- a/src/beman/timed_lock_alg/CMakeLists.txt
+++ b/src/beman/timed_lock_alg/CMakeLists.txt
@@ -1,23 +1,3 @@
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-add_library(beman.timed_lock_alg)
-add_library(beman::timed_lock_alg ALIAS beman.timed_lock_alg)
-
target_sources(beman.timed_lock_alg PRIVATE mutex.cpp)
-
-target_sources(
- beman.timed_lock_alg
- PUBLIC
- FILE_SET HEADERS
- BASE_DIRS "${CMAKE_CURRENT_SOURCE_DIR}/../../../include"
- FILES
- "${CMAKE_CURRENT_SOURCE_DIR}/../../../include/beman/timed_lock_alg/mutex.hpp"
-)
-
-set_target_properties(
- beman.timed_lock_alg
- PROPERTIES VERIFY_INTERFACE_HEADER_SETS ON
-)
-
-find_package(beman-install-library REQUIRED)
-beman_install_library(beman.timed_lock_alg)
diff --git a/src/beman/timed_lock_alg/beman.timed_lock_alg-config.cmake.in b/src/beman/timed_lock_alg/beman.timed_lock_alg-config.cmake.in
deleted file mode 100644
index 6b59283..0000000
--- a/src/beman/timed_lock_alg/beman.timed_lock_alg-config.cmake.in
+++ /dev/null
@@ -1,7 +0,0 @@
-set(BEMAN_TIMED_LOCK_ALG_VERSION @PROJECT_VERSION@)
-
-@PACKAGE_INIT@
-
-include(${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@-targets.cmake)
-
-check_required_components(@PROJECT_NAME@)
diff --git a/tests/beman/timed_lock_alg/CMakeLists.txt b/tests/beman/timed_lock_alg/CMakeLists.txt
index 86c0d79..d699636 100644
--- a/tests/beman/timed_lock_alg/CMakeLists.txt
+++ b/tests/beman/timed_lock_alg/CMakeLists.txt
@@ -6,7 +6,7 @@ add_executable(beman.timed_lock_alg.tests.try_lock)
target_sources(beman.timed_lock_alg.tests.try_lock PRIVATE try_lock.test.cpp)
target_link_libraries(
beman.timed_lock_alg.tests.try_lock
- PRIVATE beman::timed_lock_alg GTest::gtest GTest::gtest_main
+ PRIVATE beman::timed_lock_alg GTest::gtest_main
)
include(GoogleTest)
@@ -19,7 +19,7 @@ target_sources(
)
target_link_libraries(
beman.timed_lock_alg.tests.multi_lock
- PRIVATE beman::timed_lock_alg GTest::gtest GTest::gtest_main
+ PRIVATE beman::timed_lock_alg GTest::gtest_main
)
include(GoogleTest)