├── .gitignore ├── CMakeLists.txt ├── LICENSE.txt ├── README.md ├── azure-pipelines.yml ├── ci.ps1 ├── ci ├── __init__.py └── fxt.py ├── conanfile.py ├── data ├── icon.svg ├── icon_128.png └── ninja ├── latest-info.cmake ├── pmm.cmake ├── pmm ├── bpt.cmake ├── cmcm.cmake ├── conan.cmake ├── dds.cmake ├── entry.cmake ├── main.cmake ├── python.cmake ├── util.cmake └── vcpkg.cmake ├── poetry.lock ├── pyproject.toml └── tests ├── BuildContainer.cmake ├── BuildTestProject.cmake ├── CMakeLists.txt ├── RunDockerTest.cmake ├── alpine.docker └── Dockerfile ├── c7.docker └── Dockerfile ├── c8.docker └── Dockerfile ├── ci ├── CMakeLists.txt ├── RunCITest.cmake ├── cmcm │ ├── CMakeLists.txt │ └── main.cpp ├── conan │ ├── CMakeLists.txt │ ├── conanfile.py │ └── main.cpp ├── vcpkg-overlay │ ├── CMakeLists.txt │ ├── custom-ports │ │ └── .empty │ ├── custom-ports2 │ │ └── nlohmann-json │ │ │ ├── portfile.cmake │ │ │ └── vcpkg.json │ └── main.cpp └── vcpkg │ ├── CMakeLists.txt │ └── main.cpp ├── conan.test-project ├── CMakeLists.txt └── conanfile.py ├── conftest.py ├── test_bpt.py ├── u18.docker └── Dockerfile ├── u20-cm3_20.docker └── Dockerfile ├── u20.docker └── Dockerfile └── vcpkg.test-project ├── CMakeLists.txt └── main.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | .python-version 3 | _pmm/ 4 | ci-build/ 5 | _build/ 6 | .idea/ 7 | .vscode/ 8 | cmake-build-debug/ 9 | __pycache__/ -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.13) 2 | project(pmm VERSION 2.1.0) 3 | 4 | include(latest-info.cmake) 5 | 6 | set(PMM_ALWAYS_DOWNLOAD TRUE) 7 | set(PMM_URL "file://${PROJECT_SOURCE_DIR}/pmm") 8 | set(PMM_DEBUG TRUE) 9 | include(pmm.cmake) 10 | pmm(CONAN BINCRAFTERS) 11 | 12 | file(GLOB_RECURSE pmm_sources CONFIGURE_DEPENDS pmm/*) 13 | set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ${pmm_sources}) 14 | 15 | option(BUILD_TESTING "Build the testing tree" ON) 16 | if(BUILD_TESTING) 17 | enable_testing() 18 | add_subdirectory(tests) 19 | endif() 20 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 vector-of-bool 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PMM - The Package Manager Manager 2 | 3 | PMM is a module for CMake that manages... package managers. 4 | 5 | ## Wha- Why? 6 | 7 | People hate installing new software. Especially when they already have a 8 | perfectly working tool present. PMM uses the CMake scripting language to manage 9 | external packaging tools. PMM will automatically download, install, and control 10 | package managers from within your CMake project. 11 | 12 | (As you are reading this, only Conan, vcpkg, CMakeCM, and bpt are supported.) 13 | 14 | ## But This is Just *Another* Tool I have to Manage! 15 | 16 | Never fear! PMM is the lowest-maintenance software you will ever use. 17 | 18 | 19 | ## How Do I Use PMM? 20 | 21 | Using PMM is simple: 22 | 23 | 1. Download the `pmm.cmake` file (available at the top level of this 24 | respository), and place it at the top level of your repository (alongside 25 | your `CMakeLists.txt`). 26 | 2. In your `CMakeLists.txt`, add a line `include(pmm.cmake)`. 27 | 3. Call the `pmm()` CMake function. 28 | 29 | That's it! The `pmm.cmake` file is just 23 significant lines. Take a look inside 30 | if you doubt. 31 | 32 | 33 | ## Wait... It's Downloading a Bunch of Stuff! 34 | 35 | Precisely! `pmm.cmake` is just a bootstrapper for the real PMM code, which can 36 | be found in the `pmm/` directory in the repository. The content is served over 37 | HTTPS from the `gh-pages` branch of the PMM repository, so it is all publicly 38 | visible. 39 | 40 | 41 | ## I Don't Want to Automatically Download and Run Code from the Internet 42 | 43 | Great! I sympathize, but remember: If you run `apt`, `yum`, `pip`, or even 44 | `conan` and `vcpkg`, you are automatically downloading and running code from the 45 | internet. It's all about whose code you *trust*. 46 | 47 | Even still, you can host the PMM code yourself: Download the `pmm/` directory as 48 | you want it, and modify the `pmm.cmake` script to download from your alternate 49 | location (eg, a corporate engineering intranet server). 50 | 51 | 52 | ## Will PMM Updates Silently Break my Build? 53 | 54 | Nope. `pmm.cmake` will never automatically change the version of PMM that it 55 | uses, and the files served will never be modified in-place: New versions will be 56 | *added,* but old versions will remain unmodified. 57 | 58 | PMM will notify you if a new version is available, but it won't be annoying 59 | about it, and you can always disable this nagging by setting 60 | `PMM_IGNORE_NEW_VERSION` before including `pmm.cmake`. 61 | 62 | 63 | ## How do I Change the PMM Version? 64 | 65 | There are two ways: 66 | 67 | 1. Set `PMM_VERSION` before including the `pmm.cmake` script. 68 | 2. Modify the `PMM_VERSION_INIT` value near the top of `pmm.cmake`. 69 | 70 | Prefer (1) for conditional/temporary version changes, and (2) for permanent 71 | version changes. 72 | 73 | 74 | ## How do I Change the Download Location for PMM? 75 | 76 | For permanent changes, set `PMM_URL` and/or `PMM_URL_BASE` in `pmm.cmake`. 77 | For temporary changes, set `PMM_URL` before including `pmm.cmake` 78 | 79 | 80 | # The `pmm()` Function 81 | 82 | The only interface to PMM (after including `pmm.cmake`) is the `pmm()` CMake 83 | function. Using it is very simple. The `VERBOSE` and `DEBUG` options enable 84 | verbose and debug logging, respectively. You may set `PMM_{DEBUG,VERBOSE}` 85 | before `include(pmm.cmake)` to enable these options globally and see information 86 | about the PMM bootstrapping process. 87 | 88 | The `pmm()` signature: 89 | 90 | ```cmake 91 | pmm( 92 | # Enable verbose logging 93 | [VERBOSE] 94 | # Enable debug logging (implies VERBOSE) 95 | [DEBUG] 96 | 97 | # Use Conan 98 | [CONAN 99 | # Set additional --setting flags 100 | [SETTINGS ...] 101 | # Set additional --option flags 102 | [OPTIONS ...] 103 | # Set environment options 104 | [ENV ...] 105 | # Set the --build option. (Default is `missing`) 106 | [BUILD ] 107 | # Ensure remotes are present before installing 108 | [REMOTES [[::no_verify] [...]]] 109 | # Enable the Bincrafters repository 110 | [BINCRAFTERS] 111 | # Tell PMM about additional files that affect the result of 'conan install' 112 | [INSTALL_DEPENDS ...] 113 | ] 114 | 115 | # Use vcpkg 116 | [VCPKG 117 | # Specify the revision of vcpkg that you want to use (required) 118 | REVISION 119 | # Specify the triplet 120 | [TRIPLET ] 121 | # Ensure the given packages are installed using vcpkg 122 | [REQUIRES [req [...]]] 123 | # Copy custom ports to the vcpkg ports directory 124 | [PORTS [dirpath [...]]] 125 | # Set port overlay directories 126 | [OVERLAY_PORTS [dirpath [...]]] 127 | # Set triplet overlay directories 128 | [OVERLAY_TRIPLETS [dirpath [...]]] 129 | ] 130 | 131 | # Use CMakeCM 132 | [CMakeCM 133 | # Either use the latest release, or specify a specific base URL to 134 | # download from 135 | {ROLLING | FROM } 136 | ] 137 | 138 | # Use bpt 139 | [BPT 140 | # Specify a toolchain. Given as the --toolchain argument to `build-deps`. 141 | # If not specified, one will be generated automatically based on the 142 | # current CMake settings. 143 | [TOOLCHAIN ] 144 | # List of dependency files. Given as --deps-file to `build-deps`. 145 | [DEP_FILES [filepath [...]]] 146 | # List of dependency specifiers. 147 | [DEPENDENCIES [dep [...]]] 148 | ] 149 | ) 150 | ``` 151 | 152 | 153 | ## `CONAN` PMM mode 154 | 155 | In `CONAN` mode, PMM will install and use Conan to manage project packages. 156 | 157 | 158 | ### PMM-Managed Conan 159 | 160 | PMM defaults to a "managed Conan" mode, where it will manage a user-local copy 161 | of Conan that it will use to perform all tasks. In managed-mode, PMM will ignore 162 | any other available Conan installation present on the system. Managed mode 163 | requires that you have a Conan-supported version of Python available for use to 164 | perform the install. 165 | 166 | Conan is installed by invoking `pip` with a single `conan` requirement 167 | specifier. The default specifier is `conan<2`, which will cause Pip to install 168 | the newest version of Conan less that 2.0. The arguments given to the Pip 169 | command can be changed by setting the `PMM_CONAN_PIP_INSTALL_ARGS` option 170 | variable beforing `include()`-ing `pmm.cmake`. 171 | 172 | To disable managed-mode and use your own Conan version, set `PMM_CONAN_MANAGED` 173 | to `FALSE`. Then PMM will instead search for a `conan` executable to use 174 | elsewhere. You can specify a specific conan executable by setting the 175 | `PMM_CONAN_EXECUTABLE` variable before including `pmm.cmake`. 176 | 177 | 178 | ### Conan in PMM 179 | 180 | PMM will use the `cmake` Conan generator (or the `cmake_multi` generator, if you 181 | are using a multi-config CMake generator (experimental)) and will define 182 | imported targets for consumption (Equivalent of `conan_basic_setup(TARGETS)`). 183 | It will also set `CMAKE_PREFIX_PATH` and `CMAKE_MODULE_PATH` for you to use 184 | `find_package()` and `include()` against the installed dependencies. 185 | 186 | > **NOTE:** No other CMake variables from regular Conan usage are defined. 187 | 188 | `CONAN` mode requires a `conanfile.txt` or `conanfile.py` in your project 189 | source directory. It will run `conan install` against this file to obtain 190 | dependencies for your project. 191 | 192 | 193 | ### PMM Will Not do _Everything_ for You 194 | 195 | While PMM will ensure that Conan has been executed for you as part of your 196 | configure stage, it is up to you to provide a Conanfile that Conan can consume 197 | to get your dependency information. 198 | 199 | You will still need to read the Conan documentation to understand the basics of 200 | how to declare and consume your dependencies. 201 | 202 | 203 | ## `VCPKG` PMM mode 204 | 205 | In `VCPKG` mode, PMM will download the vcpkg repository at the given 206 | `REVISION`, build the `vcpkg` tool, and manage the package installation in a 207 | use-local data directory. 208 | 209 | `REVISION` should be a git tree-ish (A revision number (preferred), branch, or 210 | tag) that you could `git checkout` from the vcpkg repository. PMM will download 211 | the specified commit from GitHub and build the `vcpkg` command line tool from 212 | source. **You will need `std::filesystem` or `std::experimental::filesystem` 213 | support from your compiler and standard library.** 214 | 215 | If you want to copy custom ports to the vcpkg ports folder, you can define 216 | `PORTS` with a list of folders to copy over. You can create port overlay 217 | directories and pass them with the `OVERLAY_PORTS` argument to `pmm()`. 218 | 219 | `REQUIRES` is a list of packages that you would like to install using the 220 | `vcpkg` command line tool. 221 | 222 | When using PMM, you do not need to use the `vcpkg.cmake` CMake toolchain 223 | file: PMM will take care of this aspect for you. 224 | 225 | After calling `pmm(VCPKG)`, all you need to do is `find_package()` the 226 | packages that you want to use. 227 | 228 | 229 | ## `CMakeCM` PMM mode 230 | 231 | If `CMakeCM` is provided, PMM will download and make available the [CMake 232 | Community Modules](https://github.com/vector-of-bool/CMakeCM) for you project. 233 | 234 | Once the `pmm()` function is run, you may `include` or `find_package` any of the 235 | modules provided by `CMakeCM`. 236 | 237 | You must also specify either `ROLLING` or `FROM ` to use CMakeCM with 238 | PMM: 239 | 240 | - If you specify `ROLLING`, PMM will download the latest version of the CMakeCM 241 | module index every time you configure (with a few minutes of cooldown). 242 | - If you specify `FROM`, the module index will only be obtained from the given 243 | base URL. **Note:** This URL *is not* the URL of a `CMakeCM.cmake` file: It 244 | is a url that *prefixes* the `CMakeCM.cmake` module URL. 245 | 246 | 247 | ## `BPT` PMM mode 248 | 249 | With `BPT`, PMM will automatically download and use `bpt` to install 250 | dependencies. This will result in imported targets being defined that you can 251 | then link into your project. 252 | 253 | Calling `pmm(BPT)` multiple times is allowed: Each call will *append* to the 254 | set of installed dependencies rather than override it. 255 | 256 | The current compile flags, definitions, and include directories will be used to 257 | generate a toolchain file automatically if one is not provided. 258 | 259 | The value of `CMAKE_CXX_COMPILER_LAUNCHER` will be given as the compiler 260 | launcher in the generated toolchain file. This can be override with 261 | `PMM_BPT_COMPILER_LAUNCHER`, including setting an empty string `""` to disable 262 | it completely. 263 | 264 | **NOTE**: `bpt` support is still very experimental, and `bpt` itself is still in 265 | beta at the time of this writing. Refer to [the `bpt` 266 | documentation](https://bpt.pizza/docs/) for information about using `bpt`. 267 | 268 | 269 | # Helper Commands 270 | 271 | The `pmm.cmake` script can be run with `-P` to access a set of utility 272 | subcommands and options. See `cmake -P pmm.cmake /Help` for options. 273 | Additionally, after PMM has run for the first time, it will generate a `sh` and 274 | `bat` script that can be used to access the same set of options without needing 275 | `cmake -P pmm.cmake`: 276 | 277 | Get help with the `/Help` option: 278 | 279 | ```sh 280 | > pmm-cli.sh /Help 281 | ``` 282 | 283 | As an example, you can rebuild a Conan package with this command: 284 | 285 | ```sh 286 | > pmm-cli.sh /Conan /Rebuild /BuildType 287 | ``` 288 | -------------------------------------------------------------------------------- /azure-pipelines.yml: -------------------------------------------------------------------------------- 1 | # Starter pipeline 2 | # Start with a minimal pipeline that you can customize to build and deploy your code. 3 | # Add steps that build, run tests, deploy, and more: 4 | # https://aka.ms/yaml 5 | 6 | jobs: 7 | - job: MSVC 8 | displayName: MSVC on Windows 2019 9 | pool: 10 | vmImage: windows-2019 11 | steps: 12 | - script: choco install ninja 13 | displayName: Install Ninja 14 | - script: > 15 | call "C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\Enterprise\\Common7\\Tools\\vsdevcmd" -arch=x64 && 16 | pwsh ./ci.ps1 -ForceMSVC 17 | displayName: Run tests 18 | 19 | - job: MinGW 20 | displayName: MinGW on Windows 2019 21 | pool: 22 | vmImage: windows-2019 23 | steps: 24 | - script: choco install ninja 25 | displayName: Install Ninja 26 | - script: > 27 | call "C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\Enterprise\\Common7\\Tools\\vsdevcmd" -arch=x64 && 28 | pwsh ./ci.ps1 29 | displayName: Run tests 30 | 31 | - job: LinuxDocker 32 | displayName: Linux w/ Docker 33 | pool: 34 | vmImage: ubuntu-20.04 35 | steps: 36 | - script: | 37 | wget https://github.com/Kitware/CMake/releases/download/v3.16.1/cmake-3.16.1-Linux-x86_64.sh -nv -O /tmp/cmake.sh 38 | sudo sh /tmp/cmake.sh --exclude-subdir --prefix=/usr/local 39 | sudo apt-get install -y python3-venv ninja-build 40 | displayName: Prepare System 41 | - script: pwsh ./ci.ps1 -RunDockerTests 42 | displayName: Run tests 43 | 44 | - job: macOS_10 45 | displayName: macOS 10.15 46 | pool: 47 | vmImage: macos-10.15 48 | steps: 49 | - script: | 50 | curl -Ls https://github.com/ninja-build/ninja/releases/download/v1.8.2/ninja-mac.zip -o ninja-mac.zip 51 | unzip ninja-mac.zip 52 | sudo cp -v ninja /usr/local/bin/ 53 | displayName: Prepare System 54 | - script: env CXX=g++-9 CC=gcc-9 pwsh ./ci.ps1 55 | displayName: Run tests 56 | 57 | - job: macOS_11 58 | displayName: macOS 11 59 | pool: 60 | vmImage: macos-11 61 | steps: 62 | - script: | 63 | curl -Ls https://github.com/ninja-build/ninja/releases/download/v1.8.2/ninja-mac.zip -o ninja-mac.zip 64 | unzip ninja-mac.zip 65 | sudo cp -v ninja /usr/local/bin/ 66 | displayName: Prepare System 67 | - script: env CXX=g++-9 CC=gcc-9 pwsh ./ci.ps1 68 | displayName: Run tests -------------------------------------------------------------------------------- /ci.ps1: -------------------------------------------------------------------------------- 1 | [CmdletBinding(PositionalBinding=$false)] 2 | param( 3 | # Run the Docker tests 4 | [Parameter()] 5 | [switch] 6 | $RunDockerTests, 7 | # Forcibly set CC and CXX to MSVC cl.exe 8 | [Parameter()] 9 | [switch] 10 | $ForceMSVC, 11 | # Ignore the `ci/` tests directory 12 | [Parameter()] 13 | [switch] 14 | $NoCITestDir, 15 | # Do not delete the build directory before running 16 | [Parameter()] 17 | [switch] 18 | $NoClean, 19 | # Run tests matching the given regular expression 20 | [Parameter()] 21 | [regex] 22 | $TestRegex 23 | ) 24 | 25 | $ErrorActionPreference = "Stop" 26 | 27 | if ($PSVersionTable.OS -and $PSVersionTable.OS.StartsWith("Darwin")) { 28 | # We're on macOS, and we need a newer GCC for the FS TS 29 | & brew install "gcc@9" 30 | if ($LASTEXITCODE) { 31 | throw "Brew installation failed!" 32 | } 33 | $cc = Get-ChildItem '/usr/local/Cellar/gcc@9/*/bin/gcc-9' 34 | $cxx = Get-ChildItem '/usr/local/Cellar/gcc@9/*/bin/g++-9' 35 | $env:CC = $cc.FullName 36 | $env:CXX = $cxx.FullName 37 | } 38 | 39 | if ($ForceMSVC) { 40 | $env:CC = "cl" 41 | $env:CXX = "cl" 42 | } 43 | 44 | $cmake = (Get-Command -Name cmake).Source 45 | $ninja = (Get-Command -Name ninja).Source 46 | 47 | if (! $cmake) { 48 | throw "No CMake installed?" 49 | } 50 | if (! $ninja) { 51 | throw "No Ninja found?" 52 | } 53 | 54 | $source_dir = $PSScriptRoot 55 | $bin_dir = Join-Path $source_dir "ci-build" 56 | 57 | if (-not $NoClean -and (Test-Path $bin_dir)) { 58 | Remove-Item -Recurse $bin_dir -Force 59 | } 60 | 61 | $run_docker_tests = "FALSE" 62 | if ($RunDockerTests) { 63 | $run_docker_tests = "TRUE" 64 | } 65 | $no_ci_test_dir = "FALSE" 66 | if ($NoCITestDir) { 67 | $no_ci_test_dir = "TRUE" 68 | } 69 | 70 | & $cmake -GNinja ` 71 | "-DRUN_DOCKER_TESTS:BOOL=$run_docker_tests" ` 72 | "-DNO_CI_TEST_DIR:BOOL=$no_ci_test_dir" ` 73 | "-H$source_dir" "-B$bin_dir" 74 | if ($LASTEXITCODE) { 75 | throw "CMake configure failed [$LASTEXITCODE]" 76 | } 77 | 78 | & $cmake --build $bin_dir 79 | if ($LASTEXITCODE) { 80 | throw "CMake build failed [$LASTEXITCODE]" 81 | } 82 | 83 | $cm_dir = Split-Path $cmake -Parent 84 | $ctest = Join-Path $cm_dir "ctest" 85 | $args = @() 86 | if ($VerbosePreference) { 87 | $args += "-V" 88 | } 89 | 90 | if ($TestRegex) { 91 | $args += "-R", "$TestRegex" 92 | } 93 | 94 | & $cmake -E chdir $bin_dir $ctest -j6 --output-on-failure @args 95 | if ($LASTEXITCODE) { 96 | throw "CTest failed [$LASTEXITCODE]" 97 | } 98 | -------------------------------------------------------------------------------- /ci/__init__.py: -------------------------------------------------------------------------------- 1 | """PMM CI files""" -------------------------------------------------------------------------------- /ci/fxt.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | import textwrap 3 | from contextlib import contextmanager 4 | from pathlib import Path 5 | from typing import Callable, Iterator, MutableSequence, Protocol 6 | 7 | from pytest import TempPathFactory, fixture 8 | 9 | ROOT = Path(__file__).resolve().parent.parent 10 | 11 | 12 | @fixture(scope='session') 13 | def pmm_dir() -> Path: 14 | return ROOT 15 | 16 | 17 | class CMakeProject: 18 | 19 | def __init__(self, dirpath: Path) -> None: 20 | self.root = dirpath 21 | 22 | def configure(self) -> None: 23 | cmd = ['cmake', '-S', str(self.root), '-B', str(self.root / '_build')] 24 | cmd.append(f'-DPMM_INCLUDE={ROOT / "pmm.cmake"}') 25 | cmd.append(f'-DPMM_URL={ROOT.joinpath("pmm").as_uri()}') 26 | print(f'Run command: {cmd=}') 27 | subprocess.check_call(cmd) 28 | 29 | def build(self) -> None: 30 | cmd = ['cmake', '--build', str(self.root / '_build')] 31 | subprocess.check_call(cmd) 32 | 33 | def write(self, fpath: Path | str, content: str, *, dedent: bool = True) -> None: 34 | fpath = Path(fpath) 35 | assert not fpath.is_absolute(), f'Path [{fpath=}] shouuld be a relative path' 36 | if dedent: 37 | content = textwrap.dedent(content) 38 | self.root.joinpath(fpath).write_text(content) 39 | 40 | 41 | CMakeProjectFactory = Callable[[str], CMakeProject] 42 | 43 | 44 | @fixture(scope='session') 45 | def tmp_project_factory(tmp_path_factory: TempPathFactory) -> CMakeProjectFactory: 46 | 47 | def fac(name: str) -> CMakeProject: 48 | tdir = tmp_path_factory.mktemp(f'proj-{name}') 49 | return CMakeProject(tdir) 50 | 51 | return fac 52 | -------------------------------------------------------------------------------- /conanfile.py: -------------------------------------------------------------------------------- 1 | import conans 2 | 3 | 4 | class PMM(conans.ConanFile): 5 | name = 'pmm' 6 | version = '2.1.0' 7 | settings = None 8 | exports_sources = '*' 9 | generators = 'cmake' 10 | 11 | # build_requires = ( 12 | # 'libman-generator/[*]@vector-of-bool/test' 13 | # ) 14 | # generators = 'cmake', 'LibMan' 15 | 16 | def build(self): 17 | cmake = conans.CMake(self) 18 | cmake.configure() 19 | cmake.build() 20 | -------------------------------------------------------------------------------- /data/icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 23 | 25 | 28 | 32 | 36 | 40 | 44 | 45 | 48 | 52 | 56 | 57 | 67 | 76 | 85 | 95 | 104 | 114 | 116 | 120 | 124 | 128 | 132 | 133 | 135 | 139 | 143 | 147 | 151 | 152 | 160 | 162 | 166 | 170 | 171 | 173 | 177 | 181 | 182 | 184 | 188 | 192 | 193 | 195 | 199 | 203 | 207 | 211 | 212 | 214 | 218 | 222 | 226 | 230 | 231 | 241 | 250 | 252 | 256 | 260 | 264 | 268 | 269 | 272 | 276 | 280 | 281 | 284 | 288 | 292 | 296 | 297 | 305 | 315 | 326 | 337 | 338 | 367 | 371 | 378 | 385 | 392 | 393 | 395 | 396 | 398 | image/svg+xml 399 | 401 | 402 | 403 | 404 | 405 | 410 | 418 | 425 | 434 | 435 | 439 | 446 | 453 | 460 | 466 | 472 | 478 | 485 | 490 | 497 | 498 | 502 | 507 | 512 | 517 | 522 | 527 | 532 | 537 | 542 | 547 | 552 | 557 | 558 | 559 | -------------------------------------------------------------------------------- /data/icon_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vector-of-bool/pmm/3750c8e0b84c93669f9c7054b091788715c4dc10/data/icon_128.png -------------------------------------------------------------------------------- /data/ninja: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vector-of-bool/pmm/3750c8e0b84c93669f9c7054b091788715c4dc10/data/ninja -------------------------------------------------------------------------------- /latest-info.cmake: -------------------------------------------------------------------------------- 1 | set(PMM_LATEST_VERSION 2.1.0) 2 | 3 | if(PMM_IGNORE_NEW_VERSION) 4 | return() 5 | endif() 6 | 7 | function(_pmm_changes version) 8 | if(PMM_VERSION VERSION_LESS version) 9 | message(STATUS "[pmm] - Changes in ${version}:") 10 | foreach(change IN LISTS ARGN) 11 | message(STATUS "[pmm] - ${change}") 12 | endforeach() 13 | endif() 14 | endfunction() 15 | 16 | if(PMM_VERSION VERSION_LESS PMM_LATEST_VERSION) 17 | message(STATUS "[pmm] You are using PMM version ${PMM_VERSION}. The latest is ${PMM_LATEST_VERSION}.") 18 | message(STATUS "[pmm] Changes since ${PMM_VERSION} include the following:") 19 | _pmm_changes(0.2.0 20 | "Automatic update checks" 21 | "CONAN uses the `cmake` generator and automatically defines imported targets" 22 | "SETTINGS for Conan" 23 | "OPTIONS for Conan" 24 | "BUILD for setting the Conan install '--build' option" 25 | ) 26 | _pmm_changes(0.3.0 27 | "MSVC Support and Fixes" 28 | ) 29 | _pmm_changes(0.3.1 30 | "Install virtualenv in a user-local path" 31 | ) 32 | _pmm_changes(0.4.0 33 | "Some utilities when running as `cmake -P pmm.cmake`" 34 | "Pass `build_type` setting to Conan" 35 | "[Windows] Install to Local AppData instead of Roaming" 36 | ) 37 | _pmm_changes(1.0.0 38 | "Support for vcpkg" 39 | ) 40 | _pmm_changes(1.0.1 41 | "Fixes for building a CMake project inside of the Conan local cache" 42 | ) 43 | _pmm_changes(1.0.2 44 | "DEBUG and VERBOSE logging options." 45 | ) 46 | _pmm_changes(1.0.3 47 | "Fix using Conan with a too-new GCC version: Only use the major version on GCC 5 and later" 48 | ) 49 | _pmm_changes(1.1.0 50 | "Support a REMOTES argument in Conan mode to add remotes before performing installation" 51 | ) 52 | _pmm_changes(1.1.1 53 | "[experimental] Preliminary support for libman" 54 | "Now installs latest supported version of Conan rather than a specific version" 55 | ) 56 | _pmm_changes(1.2.0 57 | "Fix issues with finding Python in some Windows setups" 58 | "CMakeCM support" 59 | ) 60 | _pmm_changes(1.3.0 61 | "/Install, /Upgrade, and /Uninstall arguments to the script to support controlling PMM's Conan installation" 62 | "pmm(CONAN) args: BINCRAFTERS and COMMUNITY to enable the Bincrafters and conan-community repositories, respectively" 63 | "Generate a Conan profile file in the build directory for use by the user, instead of passing all command line args" 64 | "Don't try to re-download already obtained files" 65 | "Respect PYENV_ROOT when looking for Python" 66 | "PMM DEBUG mode now prints all external commands that it executes" 67 | "PMM DEBUG prints debugging information from vcpkg bootstrap" 68 | ) 69 | _pmm_changes(1.4.0 70 | "NOTE: This includes a change for the pmm.cmake bootstrap script, which will require a manual update." 71 | "New: INSTALL_DEPENDS argument for pmm(CONAN), tells PMM about files that can affect the `conan install` result" 72 | "New: /GenProfile argument to /Conan will generate a profile on-the-fly based on CMake platform detection." 73 | "New: /BuildPolicy, /Where, /Settings, /Options, and /EnsureRemotes for /Conan" 74 | "New: /All and /Overwrite for /Conan /Upload" 75 | "Improve: More fine-grained control over Conan installation by setting the arguments passed to Pip" 76 | "Improve: Set the os_build and arch_build profile settings" 77 | "Improve: Better URL normalization for Conan remotes" 78 | "Improve: Recognize VS 2019 for Conan auto-detection" 79 | "Improve: PMM_CONAN_IGNORE_EXTERNAL_CONAN to force PMM to always install its own copy" 80 | "Improve: PMM_CONAN_PIP_ALWAYS_INSTALL to force PMM to always re-run the Pip install of Conan" 81 | "Fix: changes to build settings and options not triggering a Conan installation" 82 | "Fix: Honor the `PYENV_ROOT` environment variable" 83 | "Fix: Don't race to update Conan remotes" 84 | "Fix: Parse Conan output when finding the package ref with no user/channel" 85 | "Fix: CONAN_IN_LOCAL_CACHE isn't necessarily set when being exported" 86 | ) 87 | _pmm_changes(1.4.1 88 | "New: Experimental DDS mode is now available." 89 | ) 90 | _pmm_changes(1.4.2 91 | "Improve: Update to DDS alpha.3" 92 | ) 93 | _pmm_changes(1.4.3 94 | "Improve: Update to DDS alpha.4" 95 | ) 96 | _pmm_changes(1.5.0 97 | "New: Support the 'cmake_multi' generator with Conan (experimental)" 98 | "New: /Conan /Clean to run 'conan remove -fsb *'" 99 | "New: IMPORT argument to pmm(DDS) will automatically call import_packages() in some cases" 100 | "New: Running pmm() will generate pmm-cli.bat and pmm-cli.sh shell scripts to manage PMM" 101 | "New: pmm(VCPKG) supports a PORTS argument, to provide custom vcpkg port files." 102 | "Improve: Update to DDS alpha.5" 103 | "Improve: Automatically propagate the MSVC_RUNTIME_LIBRARY for dds dependency builds" 104 | "Improve: CMAKE_CXX_COMPILER_LAUNCHER sets 'compiler_launcher' in the toolchain" 105 | " (Can be overridden with PMM_DDS_COMPILER_LAUNCHER)" 106 | "Fix: Generated CMakeCM paths using bad path separators" 107 | "Fix: Multiple calls to pmm(DDS) are allowed, and accumulate deps rather than overriding them" 108 | ) 109 | _pmm_changes(1.5.1 110 | "Fix: Unable to run in script mode due to define_property() calls" 111 | "Fix: Generated Conan profiles without respecting the CMAKE_BUILD_TYPE" 112 | ) 113 | _pmm_changes(2.0.0 114 | "REVIVAL: A new major version, with some important behavioral changes." 115 | "IMPORTANT: This change requires updating the 'pmm.cmake' script in your project." 116 | "IMPORTANT: Support for Python 2 and Conan older than 1.40.0 is deprecated" 117 | "IMPORTANT: Support for CMake older than 3.13.0 has been dropped." 118 | "New: PMM generates scripts in the build directory that can be used to execute the " 119 | " associated tools that were provisioned by PMM (Conan, vcpkg, dds)." 120 | "Change: PMM now always installs its own copy of Conan unless asked to" 121 | " use the system version." 122 | "Change: PMM will not automatically install new Conan versions when they are released." 123 | "Fix: PMM uses the updated Bincrafters repository URL" 124 | "Change: CONAN_COMMUNITY is now a no-op" 125 | "New: VCPKG mode supports OVERLAY_PORTS and OVERLAY_TRIPLETS settings" 126 | "Fix: Pass '--recurse' during 'vcpkg install'" 127 | "Fix: Concurrent vcpkg bootstrapping is now guarded with a lock" 128 | "Change: Use dds Alpha 6" 129 | "Meta: Improved test case coverage will ensure greater stability and maintainability." 130 | "Meta: pmm.cmake now has better inline docs to document its behavior and usage." 131 | ) 132 | _pmm_changes(2.1.0 133 | "Change: The managed Conan is now *unpinned*! Conan version may be automatically upgraded." 134 | " If you require a specific version, use PMM_CONAN_PIP_INSTALL_ARGS." 135 | "Change: DDS mode is deprecated. BPT mode is now here. Uses bpt-1.0.0-beta.1." 136 | "Change: Recommended vcpkg has been updated." 137 | ) 138 | message(STATUS "[pmm] To update, simply change the value of PMM_VERSION_INIT in pmm.cmake") 139 | message(STATUS "[pmm] You can disable these messages by setting PMM_IGNORE_NEW_VERSION to TRUE before including pmm.cmake") 140 | endif() 141 | 142 | if(NOT DEFINED _PMM_BOOTSTRAP_VERSION OR _PMM_BOOTSTRAP_VERSION LESS 4) 143 | message(STATUS "[pmm] NOTE: pmm.cmake has changed! Please download a new pmm.cmake from the PMM repository.") 144 | endif() 145 | -------------------------------------------------------------------------------- /pmm.cmake: -------------------------------------------------------------------------------- 1 | #[[ 2 | MIT License 3 | 4 | Copyright (c) 2021 vector-of-bool 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | 24 | //////////////////////////////////////////////////////////////////////////////// 25 | 26 | PPPPPPPPPPPPPPPP MMMMMM MMMMMM MMMMMM MMMMMM 27 | P:::::::::::::::P M:::::M M:::::M M:::::M M:::::M 28 | P:::::PPPPPP:::::P M::::::M M::::::M M::::::M M::::::M 29 | PP::::P P:::::P M:::::::M M:::::::M M:::::::M M:::::::M 30 | P:::P P:::::P M::::::::M M::::::::M M::::::::M M::::::::M 31 | P:::P P:::::P M:::::::::M M:::::::::M M:::::::::M M:::::::::M 32 | P:::PPPPPP:::::P M:::::M::::M M::::M:::::M M:::::M::::M M::::M:::::M 33 | P::::::::::::PP M::::M M::::M M::::M M::::M M::::M M::::M M::::M M::::M 34 | P:::PPPPPPPPP M::::M M::::M::::M M::::M M::::M M::::M::::M M::::M 35 | P:::P M::::M M:::::::M M::::M M::::M M:::::::M M::::M 36 | P:::P M::::M M:::::M M::::M M::::M M:::::M M::::M 37 | P:::P M::::M MMMMM M::::M M::::M MMMMM M::::M 38 | PP:::::PP M::::M M::::M M::::M M::::M 39 | P:::::::P M::::M M::::M M::::M M::::M 40 | P:::::::P M::::M M::::M M::::M M::::M 41 | PPPPPPPPP MMMMMM MMMMMM MMMMMM MMMMMM 42 | 43 | ]] 44 | 45 | #[[ Version: 46 | 47 | 888 888 d8b 48 | 888 888 Y8P 49 | 888 888 50 | Y88b d88P .d88b. 888d888 .d8888b 888 .d88b. 88888b. d8b 51 | Y88b d88P d8P Y8b 888P" 88K 888 d88""88b 888 "88b Y8P 52 | Y88o88P 88888888 888 "Y8888b. 888 888 888 888 888 53 | Y888P Y8b. 888 X88 888 Y88..88P 888 888 d8b 54 | Y8P "Y8888 888 88888P' 888 "Y88P" 888 888 Y8P 55 | 56 | The version of PMM that will be downloaded if no PMM_VERSION was specified 57 | before include()-ing this file. You can change this version here to update 58 | the version of PMM that is used by your project: ]] 59 | 60 | set(PMM_VERSION_INIT "2.1.0") 61 | 62 | # (See the README below for more information) 63 | # 64 | 65 | #[[ README: 66 | 67 | 8888888b. 888 888b d888 68 | 888 Y88b 888 8888b d8888 69 | 888 888 888 88888b.d88888 70 | 888 d88P .d88b. 8888b. .d88888 888Y88888P888 .d88b. d8b 71 | 8888888P" d8P Y8b "88b d88" 888 888 Y888P 888 d8P Y8b Y8P 72 | 888 T88b 88888888 .d888888 888 888 888 Y8P 888 88888888 73 | 888 T88b Y8b. 888 888 Y88b 888 888 " 888 Y8b. d8b 74 | 888 T88b "Y8888 "Y888888 "Y88888 888 888 "Y8888 Y8P 75 | 76 | #################################################################### 77 | 78 | This script is the "entrypoint" for PMM. 79 | 80 | This file should be COPIED and COMMITTED into the source tree of the project 81 | that wishes to use PMM. Do not use a file(DOWNLOAD), FetchContent(), or other 82 | pre-build script to download this file: It is intended to live in the source 83 | tree of its users and very rarely be manually updated. This file is 84 | in-and-of-itself a FetchContent()-like script that will download and import 85 | the full PMM code. 86 | 87 | This script will not automatically upgrade the version of PMM that is imported 88 | unless you specifically request it. You can be assured that the PMM code being 89 | pulled is deterministic over time. 90 | 91 | The version of PMM that is bootstrapped by this script is controlled by a 92 | single CMake variable 'PMM_VERSION' that can be set before include()-ing this 93 | file. Additionally, the 'set(PMM_VERSION_INIT)' line above this readme can be 94 | modified to control the PMM version. 95 | 96 | HINT: You can run this script with CMake directly via 'cmake -P pmm.cmake' for 97 | additional command-line functionality. ]] 98 | 99 | 100 | #[[ Options: 101 | 102 | .d88888b. 888 d8b 103 | d88P" "Y88b 888 Y8P 104 | 888 888 888 105 | 888 888 88888b. 888888 888 .d88b. 88888b. .d8888b d8b 106 | 888 888 888 "88b 888 888 d88""88b 888 "88b 88K Y8P 107 | 888 888 888 888 888 888 888 888 888 888 "Y8888b. 108 | Y88b. .d88P 888 d88P Y88b. 888 Y88..88P 888 888 X88 d8b 109 | "Y88888P" 88888P" "Y888 888 "Y88P" 888 888 88888P' Y8P 110 | 888 111 | 888 112 | 888 113 | 114 | ############################################################## 115 | 116 | The lines below set options that control how PMM is bootstrapped. These 117 | options can all be tweaked in one of two ways: 118 | 119 | - Change the default value of the option here in pmm.cmake in the associated 120 | call to pmm_option(). This is useful if you want to make a customized 121 | version of PMM and pmm.cmake to distribute and reuse between projects. 122 | 123 | - Set the associated variable before include()-ing this file. This is 124 | preferred for temporary changes, or for one-off projects that need custom 125 | values. 126 | 127 | The average user will not need to modify any variables except PMM_VERSION. 128 | 129 | Each option is documented below. Additional options and controls for PMM are 130 | documented in the README of the PMM repository. 131 | 132 | ]] 133 | 134 | # (Helpful macro to set a variable if it isn't already set) 135 | macro(pmm_option varname) 136 | if(NOT DEFINED "${varname}" AND NOT DEFINED "CACHE{${varname}}") 137 | set("${varname}" "${ARGN}") 138 | endif() 139 | endmacro() 140 | 141 | #[[ PMM_VERSION 142 | 143 | d8888b. .88b d88. .88b d88. db db d88888b d8888b. .d8888. d888888b .d88b. d8b db 144 | 88 `8D 88'YbdP`88 88'YbdP`88 88 88 88' 88 `8D 88' YP `88' .8P Y8. 888o 88 145 | 88oodD' 88 88 88 88 88 88 Y8 8P 88ooooo 88oobY' `8bo. 88 88 88 88V8o 88 146 | 88~~~ 88 88 88 88 88 88 `8b d8' 88~~~~~ 88`8b `Y8b. 88 88 88 88 V8o88 147 | 88 88 88 88 88 88 88 `8bd8' 88. 88 `88. db 8D .88. `8b d8' 88 V888 148 | 88 YP YP YP YP YP YP C88888D YP Y88888P 88 YD `8888Y' Y888888P `Y88P' VP V8P 149 | 150 | 'PMM_VERSION' will controls what version of PMM will be bootstrapped by this 151 | file. 152 | 153 | The initial value is controlled by 'PMM_VERSION_INIT', which is defined near 154 | the top of this file. Permanently updating the PMM version used by the 155 | project should be done by modifying 'PMM_VERSION_INIT' above directly rather 156 | than setting 'PMM_VERSION' 157 | 158 | ]] 159 | 160 | pmm_option(PMM_VERSION ${PMM_VERSION_INIT}) 161 | 162 | #[[ PMM_URL_BASE 163 | 164 | d8888b. .88b d88. .88b d88. db db d8888b. db d8888b. .d8b. .d8888. d88888b 165 | 88 `8D 88'YbdP`88 88'YbdP`88 88 88 88 `8D 88 88 `8D d8' `8b 88' YP 88' 166 | 88oodD' 88 88 88 88 88 88 88 88 88oobY' 88 88oooY' 88ooo88 `8bo. 88ooooo 167 | 88~~~ 88 88 88 88 88 88 88 88 88`8b 88 88~~~b. 88~~~88 `Y8b. 88~~~~~ 168 | 88 88 88 88 88 88 88 88b d88 88 `88. 88booo. 88 8D 88 88 db 8D 88. 169 | 88 YP YP YP YP YP YP C88888D ~Y8888P' 88 YD Y88888P C88888D Y8888P' YP YP `8888Y' Y88888P 170 | 171 | 'PMM_URL_BASE' sets the root URL from which all versions of PMM can be 172 | downloaded. This value is used to construct 'PMM_URL'. 173 | 174 | ]] 175 | 176 | pmm_option(PMM_URL_BASE "https://vector-of-bool.github.io/pmm") 177 | 178 | #[[ PMM_URL 179 | 180 | d8888b. .88b d88. .88b d88. db db d8888b. db 181 | 88 `8D 88'YbdP`88 88'YbdP`88 88 88 88 `8D 88 182 | 88oodD' 88 88 88 88 88 88 88 88 88oobY' 88 183 | 88~~~ 88 88 88 88 88 88 88 88 88`8b 88 184 | 88 88 88 88 88 88 88 88b d88 88 `88. 88booo. 185 | 88 YP YP YP YP YP YP C88888D ~Y8888P' 88 YD Y88888P 186 | 187 | 'PMM_URL' is a URL that points to the "directory" that contains the PMM 188 | source code for a given version. 189 | 190 | The default value is the subdirectory of '${PMM_URL_BASE}' with a name of 191 | '${PMM_VERSION}'. 192 | 193 | ]] 194 | 195 | pmm_option(PMM_URL "${PMM_URL_BASE}/${PMM_VERSION}") 196 | 197 | #[[ PMM_DIR 198 | 199 | d8888b. .88b d88. .88b d88. d8888b. d888888b d8888b. 200 | 88 `8D 88'YbdP`88 88'YbdP`88 88 `8D `88' 88 `8D 201 | 88oodD' 88 88 88 88 88 88 88 88 88 88oobY' 202 | 88~~~ 88 88 88 88 88 88 88 88 88 88`8b 203 | 88 88 88 88 88 88 88 88 .8D .88. 88 `88. 204 | 88 YP YP YP YP YP YP C88888D Y8888D' Y888888P 88 YD 205 | 206 | 'PMM_DIR' is the base directory where PMM will write the bootstrapped copy 207 | of PMM. The default is a subdirectory of the CMake build directory qualified 208 | by the version of PMM that was downloaded. 209 | 210 | ]] 211 | 212 | pmm_option(PMM_DIR "${CMAKE_BINARY_DIR}/_pmm/${PMM_VERSION}") 213 | 214 | #[[ PMM_MODULE 215 | 216 | d8888b. .88b d88. .88b d88. .88b d88. .d88b. d8888b. db db db d88888b 217 | 88 `8D 88'YbdP`88 88'YbdP`88 88'YbdP`88 .8P Y8. 88 `8D 88 88 88 88' 218 | 88oodD' 88 88 88 88 88 88 88 88 88 88 88 88 88 88 88 88 88ooooo 219 | 88~~~ 88 88 88 88 88 88 88 88 88 88 88 88 88 88 88 88 88~~~~~ 220 | 88 88 88 88 88 88 88 88 88 88 `8b d8' 88 .8D 88b d88 88booo. 88. 221 | 88 YP YP YP YP YP YP C88888D YP YP YP `Y88P' Y8888D' ~Y8888P' Y88888P Y88888P 222 | 223 | 'PMM_MODULE' is the absolute path to the pmm.cmake script (this file). It 224 | should not be changed or set unless you *really* know what you are doing. 225 | 226 | ]] 227 | 228 | pmm_option(PMM_MODULE "${CMAKE_CURRENT_LIST_FILE}") 229 | 230 | ################################################################################ 231 | ################################################################################ 232 | ################################################################################ 233 | ################################################################################ 234 | 235 | #[[ 236 | 237 | The code below is the implementation of the bootstrapping process for PMM. 238 | You should not change any of the code below. 239 | 240 | ]] 241 | 242 | # The file that we first download 243 | set(_PMM_ENTRY_FILE "${PMM_DIR}/entry.cmake") 244 | 245 | # Guard against multiple processes trying to use the PMM dir simultaneously 246 | file(LOCK "${PMM_DIR}/_init-pmm" 247 | GUARD PROCESS 248 | TIMEOUT 10 249 | RESULT_VARIABLE _lock_res 250 | ) 251 | if(NOT _lock_res STREQUAL "0") 252 | message(WARNING "PMM entry didn't lock the directory [${PMM_DIR}] successfully (${_lock_res}). We'll continue as best we can.") 253 | set(_pmm_init_did_lock FALSE) 254 | else() 255 | set(_pmm_init_did_lock TRUE) 256 | endif() 257 | 258 | if(NOT EXISTS "${_PMM_ENTRY_FILE}" OR PMM_ALWAYS_DOWNLOAD) 259 | file( 260 | DOWNLOAD "${PMM_URL}/entry.cmake" 261 | "${_PMM_ENTRY_FILE}.tmp" 262 | STATUS pair 263 | ) 264 | list(GET pair 0 rc) 265 | list(GET pair 1 msg) 266 | if(rc) 267 | message(FATAL_ERROR "Failed to download PMM entry file: ${msg}") 268 | endif() 269 | file(RENAME "${_PMM_ENTRY_FILE}.tmp" "${_PMM_ENTRY_FILE}") 270 | endif() 271 | 272 | set(_PMM_BOOTSTRAP_VERSION 4) 273 | include("${_PMM_ENTRY_FILE}") 274 | 275 | if(_pmm_init_did_lock) 276 | file(LOCK "${PMM_DIR}/_init-pmm" RELEASE) 277 | endif() 278 | -------------------------------------------------------------------------------- /pmm/bpt.cmake: -------------------------------------------------------------------------------- 1 | # Regular include_guard() doesn't work because this file may be downloaded and 2 | # replaced within a single CMake config run. 3 | get_cmake_property(_was_included _PMM_BPT_CMAKE_INCLUDED) 4 | if(_was_included) 5 | return() 6 | endif() 7 | set_property(GLOBAL PROPERTY _PMM_BPT_INCLUDED TRUE) 8 | 9 | _pmm_check_and_include_file(dds.cmake) 10 | 11 | pmm_option(PMM_BPT_VERSION "1.0.0-beta.1") 12 | pmm_option(PMM_BPT_URL_BASE "https://github.com/vector-of-bool/bpt/releases/download/${PMM_BPT_VERSION}") 13 | 14 | if(NOT CMAKE_SCRIPT_MODE_FILE) 15 | # Script-mode doesn't like calling define_property() 16 | define_property(GLOBAL PROPERTY BPT_DEPENDENCIES 17 | BRIEF_DOCS "Dependencies for bpt" 18 | FULL_DOCS "The accumulated list of dependencies that have been requested via pmm(BPT) with the DEPENDENCIES argument" 19 | ) 20 | define_property(GLOBAL PROPERTY BPT_DEP_FILES 21 | BRIEF_DOCS "Dependency files for bpt" 22 | FULL_DOCS "The accumulated list of dependency JSON5 files that have been requested via pmm(BPT) with the DEP_FILES argument" 23 | ) 24 | endif() 25 | 26 | set_property(GLOBAL PROPERTY BPT_DEPENDENCIES "") 27 | set_property(GLOBAL PROPERTY BPT_DEP_FILES "") 28 | 29 | 30 | function(_pmm_get_bpt_exe out) 31 | if(DEFINED PMM_BPT_EXECUTABLE) 32 | _pmm_log("Using user-specified BPT executable: ${PMM_BPT_EXECUTABLE}") 33 | set("${out}" "${PMM_BPT_EXECUTABLE}" PARENT_SCOPE) 34 | return() 35 | endif() 36 | get_cmake_property(bpt_exe _PMM_BPT_EXE) 37 | if(bpt_exe) 38 | set("${out}" "${bpt_exe}" PARENT_SCOPE) 39 | return() 40 | endif() 41 | set(sysname "${CMAKE_HOST_SYSTEM_NAME}") 42 | if(sysname MATCHES "^Windows") 43 | set(bpt_dest "${PMM_DIR}/bpt.exe") 44 | set(bpt_fname "bpt-win-x64.exe") 45 | elseif(sysname STREQUAL "Linux") 46 | set(bpt_dest "${PMM_DIR}/bpt") 47 | set(bpt_fname "bpt-linux-x64") 48 | elseif(sysname STREQUAL "Darwin") 49 | set(bpt_dest "${PMM_DIR}/bpt") 50 | set(bpt_fname "bpt-macos-x64") 51 | elseif(sysname STREQUAL "FreeBSD") 52 | set(bpt_dest "${PMM_DIR}/bpt") 53 | set(bpt_fname "bpt-freebsd-x64") 54 | else() 55 | message(FATAL_ERROR "We are unnable to automatically download a bpt executable for this system.") 56 | endif() 57 | pmm_option(PMM_BPT_FILENAME "${bpt_fname}") 58 | pmm_option(PMM_BPT_URL "${PMM_BPT_URL_BASE}/${PMM_BPT_FILENAME}") 59 | if(NOT EXISTS "${bpt_dest}") 60 | # Download to a temporary location 61 | set(bpt_tempfile "${PMM_DIR}/tmp") 62 | get_filename_component(bpt_fname "${bpt_dest}" NAME) 63 | set(bpt_tempfile "${bpt_tempfile}/${bpt_fname}") 64 | _pmm_log(VERBOSE "Downloading bpt from [${bpt_url}]") 65 | _pmm_download("${PMM_BPT_URL}" "${bpt_tempfile}") 66 | # Copy the file to its destination with the execute permission bits 67 | get_filename_component(bpt_dest_dir "${bpt_dest}" DIRECTORY) 68 | file( 69 | COPY "${bpt_tempfile}" 70 | DESTINATION "${bpt_dest_dir}" 71 | FILE_PERMISSIONS 72 | OWNER_READ OWNER_WRITE OWNER_EXECUTE 73 | GROUP_READ GROUP_EXECUTE 74 | WORLD_READ WORLD_EXECUTE 75 | ) 76 | endif() 77 | set_property(GLOBAL PROPERTY _PMM_BPT_EXE "${bpt_dest}") 78 | _pmm_log(DEBUG "Local bpt executable: [${bpt_dest}]") 79 | set("${out}" "${bpt_dest}" PARENT_SCOPE) 80 | endfunction() 81 | 82 | 83 | function(_pmm_bpt) 84 | _pmm_log(WARNING "bpt support is experimental! Don't rely on this for critical systems!") 85 | _pmm_parse_args( 86 | -hardcheck 87 | - TOOLCHAIN 88 | + DEP_FILES DEPENDENCIES 89 | ) 90 | _pmm_get_bpt_exe(bpt_exe) 91 | _pmm_generate_shim(bpt "${bpt_exe}") 92 | 93 | # The user may call pmm(BPT) multiple times, in which case we append to the 94 | # dependencies as we import them, rather than replacing the libman index 95 | # with the new set of dependencies. 96 | set_property(GLOBAL APPEND PROPERTY BPT_DEPENDENCIES ${ARG_DEPENDENCIES}) 97 | set_property(GLOBAL APPEND PROPERTY BPT_DEP_FILES ${ARG_DEP_FILES}) 98 | 99 | # Get the total accumulated set of dependencies/dep-files 100 | get_cmake_property(acc_depends BPT_DEPENDENCIES) 101 | get_cmake_property(acc_dep_files BPT_DEP_FILES) 102 | 103 | # Build the command-line arguments to use with build-deps 104 | set(bdeps_args ${acc_depends}) 105 | foreach(fname IN LISTS acc_dep_files) 106 | get_filename_component(deps_fpath "${fname}" ABSOLUTE) 107 | list(APPEND bdeps_args "--deps-file=${deps_fpath}") 108 | endforeach() 109 | 110 | if(NOT ARG_TOOLCHAIN) 111 | # If the user didn't specify a toolchain, generate one now based on the 112 | # CMake environment 113 | _pmm_dds_generate_toolchain(ARG_TOOLCHAIN) 114 | endif() 115 | 116 | set(inc_file "${PROJECT_BINARY_DIR}/_bpt-deps.cmake") 117 | list(APPEND bdeps_args "--cmake=${inc_file}") 118 | list(APPEND bdeps_args "--toolchain=${ARG_TOOLCHAIN}") 119 | 120 | _pmm_exec( 121 | "${bpt_exe}" build-deps ${bdeps_args} 122 | NO_EAT_OUTPUT 123 | WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" 124 | ) 125 | if(_PMM_RC) 126 | message(FATAL_ERROR "bpt failed to build our dependencies [${_PMM_RC}]") 127 | endif() 128 | include("${inc_file}") 129 | endfunction() 130 | -------------------------------------------------------------------------------- /pmm/cmcm.cmake: -------------------------------------------------------------------------------- 1 | function(_pmm_cmcm) 2 | _pmm_parse_args( 3 | . ROLLING 4 | - FROM 5 | ) 6 | if(NOT ARG_FROM) 7 | if(NOT ARG_ROLLING) 8 | message(FATAL_ERROR "You must specify either ROLLING or FROM for CMakeCM") 9 | endif() 10 | set(ARG_FROM "https://vector-of-bool.github.io/CMakeCM") 11 | else() 12 | if(ARG_ROLLING) 13 | message(FATAL_ERROR "Cannot specify both ROLLING and FROM for CMakeCM") 14 | endif() 15 | endif() 16 | string(MD5 hash "${ARG_FROM}") 17 | string(SUBSTRING "${hash}" 0 6 hash) 18 | get_filename_component(_cmcm_index_cmake "${_PMM_USER_DATA_DIR}/${hash}-CMakeCM.cmake" ABSOLUTE) 19 | _pmm_log(VERBOSE "CMakeCM index is ${_cmcm_index_cmake}") 20 | set(download_index FALSE) 21 | set(have_file FALSE) 22 | if(NOT EXISTS "${_cmcm_index_cmake}") 23 | set(download_index TRUE) 24 | else() 25 | set(have_file TRUE) 26 | file(TIMESTAMP "${_cmcm_index_cmake}" mtime "%s") 27 | string(TIMESTAMP now "%s") 28 | math(EXPR age "${now} - ${mtime}") 29 | if(age GREATER 180) 30 | set(download_index TRUE) 31 | endif() 32 | endif() 33 | if(download_index) 34 | _pmm_download( 35 | "${ARG_FROM}/CMakeCM.cmake" 36 | "${_cmcm_index_cmake}" 37 | NO_CHECK 38 | RESULT_VARIABLE 39 | did_download 40 | ) 41 | if(NOT did_download) 42 | if(have_file) 43 | _pmm_log(WARNING "Didn't download the CMakeCM index. Using already-downloaded version") 44 | else() 45 | message(SEND_ERROR "Failed to download CMakeCM index") 46 | endif() 47 | endif() 48 | endif() 49 | set(CMCM_LOCAL_RESOLVE_URL "${ARG_FROM}") 50 | set(CMCM_MODULE_DIR "${_PMM_USER_DATA_DIR}/cmcm-${hash}/modules") 51 | file(TO_CMAKE_PATH "${CMCM_MODULE_DIR}" CMCM_MODULE_DIR) 52 | include("${_cmcm_index_cmake}") 53 | list(APPEND CMAKE_MODULE_PATH "${CMCM_MODULE_DIR}") 54 | _pmm_lift(CMAKE_MODULE_PATH) 55 | endfunction() 56 | -------------------------------------------------------------------------------- /pmm/conan.cmake: -------------------------------------------------------------------------------- 1 | pmm_option(PMM_CONAN_PIP_INSTALL_ARGS "conan<2") 2 | pmm_option(PMM_CONAN_MANAGED TRUE) 3 | pmm_option(PMM_CONAN_FORCE_REINSTALL FALSE) 4 | pmm_option(PMM_CONAN_MANAGED_NO_INSTALL FALSE) 5 | option(PMM_CMAKE_MULTI "Use the cmake_multi generator for Conan" ON) 6 | 7 | # Get Conan in a new virtualenv using the Python interpreter specified by the 8 | # py_name and py_exe arguments 9 | function(_pmm_conan_venv_install py_name py_exe) 10 | set(msg "Get Conan with ${py_name}") 11 | _pmm_log("${msg}") 12 | _pmm_log("${msg} - Using Python: ${py_exe} ") 13 | 14 | # Try to find a virtualenv module 15 | unset(venv_mod) 16 | foreach(cand IN ITEMS venv virtualenv) 17 | _pmm_log(DEBUG "${msg} - Checking for executable Python module '${cand}'") 18 | _pmm_exec("${py_exe}" -m ${cand} --help) 19 | if(NOT _PMM_RC) 20 | set(venv_mod ${cand}) 21 | break() 22 | endif() 23 | endforeach() 24 | if(NOT DEFINED venv_mod) 25 | _pmm_log("${msg} - Fail: No virtualenv module") 26 | return() 27 | endif() 28 | _pmm_log(VERBOSE "${msg} using Python virtualenv module '${venv_mod}'") 29 | 30 | # Now create a new virtualenv 31 | file(REMOVE_RECURSE "${_PMM_CONAN_MANAGED_VENV_DIR}") 32 | # Create the parent of the virtualenv directory. 33 | get_filename_component(pardir "${_PMM_CONAN_MANAGED_VENV_DIR}" DIRECTORY) 34 | file(MAKE_DIRECTORY "${pardir}") 35 | _pmm_log("${msg} - Create virtualenv") 36 | _pmm_exec("${py_exe}" -m ${venv_mod} "${_PMM_CONAN_MANAGED_VENV_DIR}") 37 | if(_PMM_RC) 38 | _pmm_log(WARNING "Error while trying to create virtualenv [${_PMM_RC}]:\n${_PMM_OUTPUT}") 39 | _pmm_log("${msg} - Fail: Could not create virtualenv") 40 | return() 41 | endif() 42 | _pmm_log(VERBOSE "Created Conan virtualenv in ${_PMM_CONAN_MANAGED_VENV_DIR}") 43 | 44 | # Get the Python installed therein 45 | unset(_venv_py CACHE) 46 | find_program(_venv_py 47 | NAMES python 48 | NO_DEFAULT_PATH 49 | PATHS "${_PMM_CONAN_MANAGED_VENV_DIR}" 50 | PATH_SUFFIXES bin Scripts 51 | ) 52 | set(venv_py "${_venv_py}") 53 | unset(_venv_py CACHE) 54 | 55 | # Upgrade pip installation 56 | _pmm_log("${msg} - Upgrade Pip") 57 | _pmm_exec("${venv_py}" -m pip install -qU pip setuptools) 58 | if(_PMM_RC) 59 | _pmm_log(WARNING "Failed while upgrading Pip in the virtualenv [${_PMM_RC}]:\n${_PMM_OUTPUT}") 60 | _pmm_log("${msg} - Fail: Pip could not be upgraded") 61 | return() 62 | endif() 63 | 64 | # Finally, install Conan inside the virtualenv. 65 | _pmm_log("${msg} - Install Conan") 66 | _pmm_exec("${venv_py}" -m pip install -Uq ${PMM_CONAN_PIP_INSTALL_ARGS}) 67 | if(_PMM_RC) 68 | _pmm_log(WARNING "Failed to install Conan in virtualenv [${_PMM_RC}]:\n${_PMM_OUTPUT}") 69 | _pmm_log("${msg} - Fail: Could not install Conan in virtualenv") 70 | return() 71 | endif() 72 | 73 | # Conan is installed! Set PMM_CONAN_EXECUTABLE 74 | unset(PMM_CONAN_EXECUTABLE CACHE) 75 | find_program( 76 | PMM_CONAN_EXECUTABLE conan 77 | NO_DEFAULT_PATH 78 | PATHS "${_PMM_CONAN_MANAGED_VENV_DIR}" 79 | PATH_SUFFIXES bin Scripts 80 | DOC "Path to the PMM-managed Conan executable" 81 | ) 82 | if(NOT PMM_CONAN_EXECUTABLE) 83 | _pmm_log(WARNING "Conan executable was not found after Conan installation. Huh??") 84 | _pmm_log("${msg} - Fail: No conan executable in Conan installation?") 85 | else() 86 | _pmm_log("${msg} - Installed: ${PMM_CONAN_EXECUTABLE}") 87 | endif() 88 | endfunction() 89 | 90 | 91 | function(_pmm_conan_vars) 92 | string(MD5 inst_cmd_hash "${PMM_CONAN_PIP_INSTALL_ARGS}") 93 | string(SUBSTRING "${inst_cmd_hash}" 0 6 inst_cmd_hash) 94 | get_filename_component(_PMM_CONAN_MANAGED_VENV_DIR "${_PMM_USER_DATA_DIR}/conan/venvs/${inst_cmd_hash}" ABSOLUTE) 95 | _pmm_lift(_PMM_CONAN_MANAGED_VENV_DIR) 96 | endfunction() 97 | 98 | 99 | function(_pmm_conan_managed_ensure_installed) 100 | set(must_reinstall FALSE) 101 | if(NOT PMM_CONAN_EXECUTABLE) 102 | _pmm_log(DEBUG "No PMM_CONAN_EXECUTABLE set while in managed-mode") 103 | elseif(NOT EXISTS "${PMM_CONAN_EXECUTABLE}") 104 | _pmm_log("Conan executable from previous run '${PMM_CONAN_EXECUTABLE}' is missing. " 105 | "A new Conan virtualenv must be created") 106 | set(must_reinstall TRUE) 107 | endif() 108 | 109 | # Before we continue, lock access to the virtualenv 110 | _pmm_log(DEBUG "PMM Conan venv directory is [${_PMM_CONAN_MANAGED_VENV_DIR}]") 111 | _pmm_verbose_lock( 112 | "${_PMM_CONAN_MANAGED_VENV_DIR}" DIRECTORY 113 | FIRST_MESSAGE "Anohter CMake instance is installing Conan. Please wait..." 114 | FAIL_MESSAGE "Unable to obtain the virtualenv lock. Check if there is a stuck process holding it open." 115 | RESULT_VARIABLE did_lock 116 | ) 117 | if(NOT did_lock) 118 | message(FATAL_ERROR "Unable to obtain exclusive lock on directory ${_PMM_CONAN_MANAGED_VENV_DIR}. Abort.") 119 | endif() 120 | 121 | if(NOT must_reinstall) 122 | if(NOT EXISTS "${_PMM_CONAN_MANAGED_VENV_DIR}") 123 | _pmm_log(DEBUG "Virtualenv does not exist") 124 | else() 125 | unset(_found CACHE) 126 | _pmm_log(DEBUG "Searching for Conan executable in existing virtualenv") 127 | find_program( 128 | _found 129 | NAMES conan 130 | PATHS "${_PMM_CONAN_MANAGED_VENV_DIR}" 131 | PATH_SUFFIXES 132 | Scripts/ 133 | bin/ 134 | NO_DEFAULT_PATH 135 | ) 136 | if(NOT _found) 137 | _pmm_log("Need to re-install Conan in a new virtualenv") 138 | set(must_reinstall TRUE) 139 | else() 140 | set(PMM_CONAN_EXECUTABLE "${_found}" CACHE FILEPATH "Managed Conan executable" FORCE) 141 | _pmm_log(VERBOSE "Managed Conan [${PMM_CONAN_EXECUTABLE}] is up-to-date") 142 | endif() 143 | unset(_found CACHE) 144 | endif() 145 | endif() 146 | 147 | if(NOT must_reinstall AND PMM_CONAN_FORCE_REINSTALL) 148 | _pmm_log("Reinstalling Conan because PMM_CONAN_FORCE_REINSTALL is '${PMM_CONAN_FORCE_REINSTALL}'") 149 | set(must_reinstall TRUE) 150 | endif() 151 | 152 | if(NOT must_reinstall) 153 | file(LOCK "${_PMM_CONAN_MANAGED_VENV_DIR}" DIRECTORY RELEASE) 154 | return() 155 | endif() 156 | 157 | if(PMM_CONAN_MANAGED_NO_INSTALL) 158 | # Caller has requested that we do not run an install. 159 | file(LOCK "${_PMM_CONAN_MANAGED_VENV_DIR}" DIRECTORY RELEASE) 160 | return() 161 | endif() 162 | 163 | _pmm_log("Installing a Conan binary...") 164 | 165 | # Let's get Conan. Let's try to get it using Python 166 | _pmm_find_python3(py3_exe) 167 | if(py3_exe) 168 | _pmm_conan_venv_install("Python 3" "${py3_exe}") 169 | else() 170 | message(FATAL_ERROR "No Python 3 was found, which is required to install Conan.") 171 | endif() 172 | if(NOT PMM_CONAN_EXECUTABLE) 173 | message(FATAL_ERROR "We failed to install Conan in a new virtualenv.") 174 | endif() 175 | file(LOCK "${_PMM_CONAN_MANAGED_VENV_DIR}" DIRECTORY RELEASE) 176 | endfunction() 177 | 178 | 179 | function(_pmm_conan_ensure_sys_present) 180 | if(PMM_CONAN_EXECUTABLE) 181 | if(EXISTS "${PMM_CONAN_EXECUTABLE}") 182 | # We have a cached binary, and it exists: Okay.s 183 | return() 184 | endif() 185 | _pmm_log(WARNING 186 | "Conan executable '${PMM_CONAN_EXECUTABLE}' from a previous " 187 | "execution is missing. We'll try to find a new one.") 188 | endif() 189 | 190 | # No cached conan location. We will now try to find an existing Conan installation 191 | 192 | # Clear the previous setting 193 | unset(PMM_CONAN_EXECUTABLE) 194 | unset(PMM_CONAN_EXECUTABLE CACHE) 195 | unset(PMM_CONAN_EXECUTABLE PARENT_SCOPE) 196 | 197 | # Load any pyenv locations that might be on the system 198 | set(pyenv_root_env "$ENV{PYENV_ROOT}") 199 | if(pyenv_root_env) 200 | file(GLOB pyenv_versions "${pyenv_root_env}/versions/*/") 201 | else() 202 | file(GLOB pyenv_versions "$ENV{HOME}/.pyenv/versions/*/") 203 | endif() 204 | _pmm_log(VERBOSE "Found pyenv installations: ${pyenv_versions}") 205 | 206 | file(GLOB py_installs C:/Python*) 207 | find_program( 208 | PMM_CONAN_EXECUTABLE conan 209 | HINTS 210 | ${pyenv_versions} 211 | PATHS 212 | "$ENV{HOME}/.local" 213 | ${py_installs} 214 | PATH_SUFFIXES 215 | . 216 | bin 217 | Scripts 218 | DOC "Path to the Conan executable" 219 | ) 220 | 221 | if(PMM_CONAN_EXECUTABLE) 222 | # We found an executable 223 | if(NOT _prev) 224 | _pmm_log("Found Conan: ${PMM_CONAN_EXECUTABLE}") 225 | endif() 226 | return() 227 | endif() 228 | 229 | # We never found anything... 230 | message(FATAL_ERROR 231 | "No Conan executable could be found on the system. " 232 | "Set PMM_CONAN_MANAGED to TRUE and PMM will install one for you.") 233 | endfunction() 234 | 235 | # Ensure the presence of a `PMM_CONAN_EXECUTABLE` program 236 | function(_pmm_ensure_conan) 237 | _pmm_conan_vars() 238 | 239 | if(PMM_CONAN_MANAGED) 240 | _pmm_conan_managed_ensure_installed() 241 | else() 242 | _pmm_conan_ensure_sys_present() 243 | endif() 244 | endfunction() 245 | 246 | 247 | function(_pmm_vs_version out) 248 | set(ver ${MSVC_VERSION}) 249 | if(ver GREATER_EQUAL 1930) 250 | _pmm_log(WARNING "PMM doesn't yet recognize this MSVC version (${ver}). You may need to upgrade PMM.") 251 | elseif(ver GREATER_EQUAL 1920) 252 | set(ret 16) 253 | elseif(ver GREATER_EQUAL 1910) 254 | set(ret 15) 255 | elseif(ver GREATER_EQUAL 1900) 256 | set(ret 14) 257 | elseif(ver GREATER_EQUAL 1800) 258 | set(ret 12) 259 | elseif(ver GREATER_EQUAL 1700) 260 | set(ret 11) 261 | elseif(ver GREATER_EQUAL 1600) 262 | set(ret 10) 263 | elseif(ver GREATER_EQUAL 1500) 264 | set(ret 9) 265 | elseif(ver GREATER_EQUAL 1400) 266 | set(ret 8) 267 | else() 268 | _pmm_log(WARNING "Unknown MSVC version: ${ver}.") 269 | set(ret 8) 270 | endif() 271 | _pmm_log(DEBUG "Calculated MSVC version to be ${ret}") 272 | set(${out} ${ret} PARENT_SCOPE) 273 | endfunction() 274 | 275 | 276 | function(_pmm_conan_uninstall) 277 | _pmm_ensure_conan() 278 | if(NOT PMM_CONAN_EXECUTABLE) 279 | _pmm_log("No Conan executable found to uninstall") 280 | return() 281 | endif() 282 | 283 | _pmm_conan_vars() 284 | if(NOT EXISTS "${_PMM_CONAN_MANAGED_VENV_DIR}") 285 | message(FATAL_ERROR "Conan executable '${PMM_CONAN_EXECUTABLE}' was not installed by PMM. We will not uninstall it.") 286 | endif() 287 | 288 | _pmm_log("Removing Conan virtualenv ${_PMM_CONAN_MANAGED_VENV_DIR}") 289 | file(REMOVE_RECURSE "${_PMM_CONAN_MANAGED_VENV_DIR}") 290 | endfunction() 291 | 292 | 293 | function(_pmm_conan_upgrade) 294 | _pmm_conan_vars() 295 | find_program(venv_py 296 | NAMES python 297 | NO_DEFAULT_PATH 298 | PATHS "${_PMM_CONAN_MANAGED_VENV_DIR}" 299 | PATH_SUFFIXES bin Scripts 300 | ) 301 | _pmm_log("Upgrading Conan...") 302 | unset(PMM_CONAN_EXECUTABLE CACHE) 303 | _pmm_exec("${venv_py}" -m pip install --quiet --upgrade ${PMM_CONAN_PIP_INSTALL_ARGS} NO_EAT_OUTPUT) 304 | if(_PMM_RC) 305 | message(FATAL_ERROR "Conan upgrade failed [${_PMM_RC}]") 306 | endif() 307 | _pmm_ensure_conan() 308 | _pmm_log("Conan upgrade successful") 309 | endfunction() 310 | 311 | 312 | function(_pmm_conan_get_settings out) 313 | _pmm_log(DEBUG "Calculating Conan settings values") 314 | set(ret) 315 | get_cmake_property(langs ENABLED_LANGUAGES) 316 | set(lang CXX) 317 | if(NOT "CXX" IN_LIST langs) 318 | set(lang C) 319 | if(NOT "C" IN_LIST langs) 320 | message(FATAL_ERROR "pmm(CONAN) requires that either C or C++ languages be enabled.") 321 | endif() 322 | endif() 323 | set(comp_id "${CMAKE_${lang}_COMPILER_ID}") 324 | set(comp_version "${CMAKE_${lang}_COMPILER_VERSION}") 325 | 326 | _pmm_log(DEBUG "Using language ${lang} compiler information (ID is ${comp_id}, version is ${comp_version})") 327 | 328 | set(majmin_ver_re "^([0-9]+\\.[0-9]+)") 329 | 330 | # Check if the user is mixing+matching compilers. 331 | if("C" IN_LIST langs AND "CXX" IN_LIST langs) 332 | if(NOT CMAKE_C_COMPILER_ID STREQUAL CMAKE_CXX_COMPILER_ID) 333 | _pmm_log(WARNING "Mixing compiler vendors for C and C++ may produce unexpected results.") 334 | else() 335 | if(NOT CMAKE_C_COMPILER_VERSION STREQUAL CMAKE_CXX_COMPILER_VERSION) 336 | _pmm_log(WARNING "Mixing compiler versions for C and C++ may produce unexpected results.") 337 | endif() 338 | endif() 339 | endif() 340 | 341 | # Detect the OS information 342 | set(sysname "${CMAKE_SYSTEM_NAME}") 343 | if(NOT sysname AND CMAKE_HOST_SYSTEM_NAME) 344 | set(sysname "${CMAKE_HOST_SYSTEM_NAME}") 345 | endif() 346 | if(sysname MATCHES "^Windows(Store|Phone)$") 347 | set(os WindowsStore) 348 | elseif(sysname STREQUAL "Linux") 349 | set(os Linux) 350 | elseif(sysname STREQUAL "Darwin") 351 | set(os Macos) 352 | elseif(sysname STREQUAL "Windows") 353 | set(os Windows) 354 | elseif(sysname STREQUAL "FreeBSD") 355 | set(os FreeBSD) 356 | endif() 357 | if(NOT ARG_SETTINGS MATCHES ";?os=") 358 | _pmm_log(DEBUG "Using os=${os}") 359 | list(APPEND ret os=${os}) 360 | endif() 361 | if(NOT ARG_SETTINGS MATCHES ";?os_build=") 362 | _pmm_log(DEBUG "Using os_build=${os}") 363 | list(APPEND ret os_build=${os}) 364 | endif() 365 | 366 | ## Check for GNU (GCC) 367 | if(comp_id STREQUAL GNU) 368 | # Use 'gcc' 369 | _pmm_log(DEBUG "Using compiler=gcc") 370 | list(APPEND ret compiler=gcc) 371 | # Parse out the version 372 | if(NOT comp_version MATCHES "${majmin_ver_re}") 373 | message(FATAL_ERROR "Unable to parse compiler version string: ${comp_version}") 374 | endif() 375 | set(use_version "${CMAKE_MATCH_1}") 376 | if(use_version VERSION_GREATER_EQUAL 5.0) 377 | string(REGEX REPLACE "^([0-9]+)\\..*" "\\1" use_version "${use_version}") 378 | endif() 379 | _pmm_log(DEBUG "Using compiler.version=${use_version}") 380 | list(APPEND ret compiler.version=${use_version}) 381 | # Detect what libstdc++ ABI are likely using. 382 | if(lang STREQUAL "CXX") 383 | if(comp_version VERSION_GREATER_EQUAL 5.1) 384 | _pmm_log(DEBUG "Using compiler.libcxx=libstdc++11") 385 | list(APPEND ret compiler.libcxx=libstdc++11) 386 | else() 387 | _pmm_log(DEBUG "Using compiler.libcxx=libstdc++") 388 | list(APPEND ret compiler.libcxx=libstdc++) 389 | endif() 390 | else() 391 | _pmm_log(DEBUG "Not setting a compiler.libcxx value (Not using C++ compiler for settings detection)") 392 | endif() 393 | ## Apple's Clang is a bit of a goob. 394 | elseif(comp_id STREQUAL AppleClang) 395 | # Use apple-clang 396 | _pmm_log(DEBUG "Using compiler=apple-clang") 397 | list(APPEND ret compiler=apple-clang) 398 | if(lang STREQUAL "CXX") 399 | _pmm_log(DEBUG "Using compiler.libcxx=libc++") 400 | list(APPEND ret compiler.libcxx=libc++) 401 | endif() 402 | # Get that version. Same as with Clang 403 | if(NOT comp_version MATCHES "${majmin_ver_re}") 404 | message(FATAL_ERROR "Unable to parse compiler version string: ${comp_version}") 405 | endif() 406 | _pmm_log(DEBUG "Using compiler.version=${CMAKE_MATCH_1}") 407 | list(APPEND ret "compiler.version=${CMAKE_MATCH_1}") 408 | # Non-Appley Clang. 409 | elseif(comp_id STREQUAL Clang) 410 | # Regular clang 411 | _pmm_log(DEBUG "Using compiler=clang") 412 | list(APPEND ret compiler=clang) 413 | # Get that version. Same as with AppleClang 414 | if(NOT comp_version MATCHES "${majmin_ver_re}") 415 | message(FATAL_ERROR "Unable to parse compiler version string: ${comp_version}") 416 | endif() 417 | 418 | set(comp_version_val ${CMAKE_MATCH_1}) 419 | if(comp_version_val VERSION_GREATER_EQUAL 8) 420 | # Conan uses major version only for clang 8+ 421 | if(NOT comp_version MATCHES "^([0-9]+)\\.") 422 | message(FATAL_ERROR "Unable to parse compiler version string, but we already parsed it: ${comp_version}") 423 | endif() 424 | set(comp_version_val ${CMAKE_MATCH_1}) 425 | endif() 426 | 427 | _pmm_log(DEBUG "Using compiler.version=${comp_version_val}") 428 | list(APPEND ret "compiler.version=${comp_version_val}") 429 | # TODO: Support libc++ with regular Clang. Plz. 430 | if(lang STREQUAL "CXX") 431 | _pmm_log(DEBUG "Using compiler.libcxx=libstdc++") 432 | list(APPEND ret compiler.libcxx=libstdc++) 433 | endif() 434 | elseif(comp_id STREQUAL "MSVC") 435 | _pmm_vs_version(vs_version) 436 | _pmm_log(DEBUG "Using compiler=Visual Studio") 437 | _pmm_log(DEBUG "Using compiler.version=${vs_version}") 438 | list(APPEND ret "compiler=Visual Studio" compiler.version=${vs_version}) 439 | if(CMAKE_GENERATOR_TOOLSET) 440 | _pmm_log(DEBUG "Using compiler.toolset=${CMAKE_GENERATOR_TOOLSET}") 441 | list(APPEND ret compiler.toolset=${CMAKE_GENERATOR_TOOLSET}) 442 | elseif(CMAKE_VS_PLATFORM_TOOLSET AND (CMAKE_GENERATOR STREQUAL "Ninja")) 443 | _pmm_log(DEBUG "Using compiler.toolset=${CMAKE_VS_PLATFORM_TOOLSET}") 444 | list(APPEND ret compiler.toolset=${CMAKE_VS_PLATFORM_TOOLSET}) 445 | endif() 446 | else() 447 | message(FATAL_ERROR "Unable to detect compiler setting for Conan from CMake. (Unhandled compiler ID ${comp_id}).") 448 | endif() 449 | 450 | # Todo: Cross compiling 451 | if(NOT ARG_SETTINGS MATCHES ";?arch=") 452 | if(CMAKE_SIZEOF_VOID_P EQUAL 8) 453 | _pmm_log(DEBUG "Using arch=${x86_64}") 454 | list(APPEND ret arch=x86_64) 455 | else() 456 | _pmm_log(DEBUG "Using arch=${x86}") 457 | list(APPEND ret arch=x86) 458 | endif() 459 | endif() 460 | if(NOT ARG_SETTINGS MATCHES ";?arch_build=") 461 | if(CMAKE_SIZEOF_VOID_P EQUAL 8) 462 | _pmm_log(DEBUG "Using arch_build=${x86_64}") 463 | list(APPEND ret arch_build=x86_64) 464 | else() 465 | _pmm_log(DEBUG "Using arch_build=${x86}") 466 | list(APPEND ret arch_build=x86) 467 | endif() 468 | endif() 469 | 470 | if(NOT (ARG_SETTINGS MATCHES ";?(compiler\\.)?cppstd=")) 471 | if(CMAKE_CXX_STANDARD) 472 | list(APPEND ret compiler.cppstd=${CMAKE_CXX_STANDARD}) 473 | endif() 474 | endif() 475 | 476 | if(CMAKE_CROSSCOMPILING) 477 | _pmm_log(WARNING "Cross compiling isn't supported yet. Be careful.") 478 | endif() 479 | 480 | set("${out}" "${ret}" PARENT_SCOPE) 481 | endfunction() 482 | 483 | 484 | function(_pmm_conan_create_profile _build_type) 485 | set(profile_lines "[settings]") 486 | 487 | # Get the settings for the profile 488 | _pmm_conan_get_settings(settings_lines) 489 | list(APPEND profile_lines "${settings_lines}") 490 | foreach(setting IN LISTS ARG_SETTINGS) 491 | list(APPEND profile_lines "${setting}") 492 | endforeach() 493 | list(APPEND profile_lines "build_type=${_build_type}") 494 | 495 | # Add the options to the profile 496 | list(APPEND profile_lines "" "[options]") 497 | foreach(arg IN LISTS ARG_OPTIONS) 498 | list(APPEND profile_lines "${arg}") 499 | endforeach() 500 | 501 | list(APPEND profile_lines "" "[env]") 502 | if(CMAKE_C_COMPILER) 503 | list(APPEND profile_lines "CC=${CMAKE_C_COMPILER}") 504 | endif() 505 | if(CMAKE_CXX_COMPILER) 506 | list(APPEND profile_lines "CXX=${CMAKE_CXX_COMPILER}") 507 | endif() 508 | foreach(env IN LISTS ARG_ENV) 509 | list(APPEND profile_lines "${env}") 510 | endforeach() 511 | 512 | string(REPLACE ";" "\n" profile_content "${profile_lines}") 513 | get_filename_component(_profile_file "${CMAKE_CURRENT_BINARY_DIR}/pmm-conan-${_build_type}.profile" ABSOLUTE) 514 | _pmm_write_if_different("${_profile_file}" "${profile_content}") 515 | set(profile_changed "${_PMM_DID_WRITE}" PARENT_SCOPE) 516 | set(profile_file ${_profile_file} PARENT_SCOPE) 517 | endfunction() 518 | 519 | function(_pmm_conan_run_install _build_type _generator_name ) 520 | # Install the thing 521 | # Do the regular install logic 522 | get_filename_component(conan_timestamp_file "${CMAKE_CURRENT_BINARY_DIR}/conaninfo.txt" ABSOLUTE) 523 | set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${conanfile}") 524 | 525 | _pmm_conan_create_profile(${_build_type}) 526 | 527 | pmm_option(ARG_BUILD missing) 528 | set(conan_args --profile "${profile_file}") 529 | list(APPEND conan_args --generator ${_generator_name} --build ${ARG_BUILD}) 530 | set(conan_install_cmd 531 | "${CMAKE_COMMAND}" -E env CONAN_LIBMAN_FOR=cmake 532 | "${PMM_CONAN_EXECUTABLE}" install "${CMAKE_CURRENT_SOURCE_DIR}" ${conan_args} 533 | ) 534 | set(prev_cmd_file "${PMM_DIR}/_prev_conan_install_cmd_${_build_type}.txt") 535 | # Check if we need to re-run the conan install 536 | set(do_install FALSE) 537 | # Check if any "install inputs" are newer 538 | set(more_inputs) 539 | if(__install_depends) 540 | file(GLOB_RECURSE more_inputs CONFIGURE_DEPENDS ${__install_depends}) 541 | endif() 542 | set(install_inputs "${__conanfile}" ${more_inputs}) 543 | foreach(inp IN LISTS install_inputs) 544 | set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${inp}") 545 | if(EXISTS "${conan_timestamp_file}" AND "${inp}" IS_NEWER_THAN "${conan_timestamp_file}") 546 | _pmm_log(DEBUG "Need to run conan install: ${inp} is newer than the last install run") 547 | set(do_install TRUE) 548 | endif() 549 | endforeach() 550 | # Check if the install has never occurred 551 | if(NOT EXISTS "${prev_cmd_file}") 552 | _pmm_log(DEBUG "Need to run conan install: Never been run") 553 | set(do_install TRUE) 554 | # Or if the profile has changed 555 | elseif(profile_changed) 556 | _pmm_log(DEBUG "Need to run conan install: Profile has changed") 557 | set(do_install TRUE) 558 | # Or if the install command has changed 559 | else() 560 | file(READ "${prev_cmd_file}" prev_cmd) 561 | if(NOT prev_cmd STREQUAL conan_install_cmd) 562 | _pmm_log(DEBUG "Need to run conan install: Install command changed from prior run.") 563 | set(do_install TRUE) 564 | endif() 565 | endif() 566 | if(NOT do_install) 567 | _pmm_log(VERBOSE "Conan installation is up-to-date. Not running Conan.") 568 | else() 569 | _pmm_log("Installing Conan requirements from ${__conanfile}") 570 | _pmm_exec(${conan_install_cmd} 571 | WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" 572 | NO_EAT_OUTPUT 573 | ) 574 | if(_PMM_RC) 575 | message(FATAL_ERROR "Conan install failed [${_PMM_RC}]:\n${_PMM_OUTPUT}") 576 | else() 577 | file(WRITE "${prev_cmd_file}" "${conan_install_cmd}") 578 | endif() 579 | endif() 580 | endfunction() 581 | 582 | 583 | macro(_pmm_conan_do_setup) 584 | _pmm_log(VERBOSE "Run conan_define_targets() and conan_set_find_paths()") 585 | conan_define_targets() 586 | conan_set_find_paths() 587 | endmacro() 588 | 589 | 590 | macro(_pmm_conan_install) 591 | if(CONAN_EXPORTED) 592 | # When we are being built by conan in the local cache directory we don't need 593 | # to do an actual conan install: It has already been done for us. 594 | set(__conan_inc "${CMAKE_CURRENT_BINARY_DIR}/conanbuildinfo.cmake") 595 | _pmm_log("We are being built by Conan, so we won't run the install step.") 596 | _pmm_log("Assuming ${__conan_inc} is present.") 597 | else() 598 | if(CMAKE_CONFIGURATION_TYPES AND NOT CMAKE_BUILD_TYPE AND PMM_CMAKE_MULTI) 599 | get_filename_component(__conan_inc "${CMAKE_CURRENT_BINARY_DIR}/conanbuildinfo_multi.cmake" ABSOLUTE) 600 | _pmm_log(WARNING "Using cmake-multi generator, this generator is experimental") 601 | _pmm_conan_run_install("Debug" "cmake_multi") 602 | _pmm_conan_run_install("Release" "cmake_multi") 603 | else() 604 | set(bt "${CMAKE_BUILD_TYPE}") 605 | if(NOT bt) 606 | _pmm_log("WARNING: CMAKE_BUILD_TYPE was not set explicitly. We'll install your dependencies as 'Debug'") 607 | set(bt "Debug") 608 | endif() 609 | get_filename_component(__conan_inc "${CMAKE_CURRENT_BINARY_DIR}/conanbuildinfo.cmake" ABSOLUTE) 610 | _pmm_conan_run_install("${bt}" "cmake") 611 | endif() 612 | endif() 613 | 614 | get_filename_component(libman_inc "${CMAKE_CURRENT_BINARY_DIR}/libman.cmake" ABSOLUTE) 615 | set(__libman_inc "${libman_inc}") 616 | 617 | _pmm_log(VERBOSE "Including Conan generated file ${__conan_inc}") 618 | include("${__conan_inc}" OPTIONAL RESULT_VARIABLE __was_included) 619 | if(NOT __was_included) 620 | message(SEND_ERROR 621 | "Conan dependencies were not imported (Expected file ${__conan_inc}). " 622 | "You may need to run Conan manually (from the build directory). " 623 | "Ensure you are using the 'cmake' generator." 624 | ) 625 | else() 626 | _pmm_conan_do_setup() 627 | endif() 628 | set(_prev_index "${LIBMAN_INDEX}") 629 | include("${__libman_inc}" OPTIONAL) 630 | if(LIBMAN_INDEX AND NOT _prev_index) 631 | _pmm_log("Libman import_packages() is available") 632 | endif() 633 | unset(__conan_inc) 634 | unset(__was_included) 635 | endmacro() 636 | 637 | 638 | function(_pmm_conan_norm_url_var varname) 639 | set(url "${${varname}}") 640 | while(url MATCHES "(.*)/+$") 641 | set(url "${CMAKE_MATCH_1}") 642 | endwhile() 643 | set("${varname}" "${url}" PARENT_SCOPE) 644 | endfunction() 645 | 646 | 647 | function(_pmm_conan_ensure_remotes remotes) 648 | file( 649 | LOCK "${_PMM_CONAN_MANAGED_VENV_DIR}/.pmm-remotes-lk" DIRECTORY 650 | GUARD FUNCTION 651 | TIMEOUT 60 652 | ) 653 | _pmm_exec("${PMM_CONAN_EXECUTABLE}" remote list) 654 | string(STRIP "${_PMM_OUTPUT}" out) 655 | string(REPLACE "\n" ";" lines "${out}") 656 | set_property(GLOBAL PROPERTY CONAN_REMOTES "") 657 | set(all_urls) 658 | foreach(line IN LISTS lines) 659 | if(line MATCHES "^(WARN|DEBUG): ") 660 | # Ignore this line 661 | elseif(NOT line MATCHES "^([^:]+): (.*) \\[Verify SSL: (.+)\]") 662 | message(WARNING "Unparseable `conan remote list` line: ${line}") 663 | else() 664 | set(name "${CMAKE_MATCH_1}") 665 | set(url "${CMAKE_MATCH_2}") 666 | _pmm_conan_norm_url_var(url) 667 | set(ssl_verify "${CMAKE_MATCH_3}") 668 | string(TOUPPER "${ssl_verify}" ssl_verify) 669 | _pmm_log(DEBUG "Found conan remote ${name} at ${url} (Verify SSL: ${ssl_verify})") 670 | set_property(GLOBAL APPEND PROPERTY CONAN_REMOTES ${name}) 671 | set_property(GLOBAL PROPERTY CONAN_REMOTE/${name}/URL ${url}) 672 | set_property(GLOBAL PROPERTY CONAN_REMOTE/${name}/SSL_VERIFY ${ssl_verify}) 673 | list(APPEND all_urls "${url}") 674 | endif() 675 | endforeach() 676 | while(TRUE) 677 | list(LENGTH remotes len) 678 | if(len EQUAL 1) 679 | message(FATAL_ERROR "REMOTES list of pmm(CONAN) must be pairs of name and value") 680 | elseif(len EQUAL 0) 681 | break() 682 | endif() 683 | list(GET remotes 0 1 head) 684 | list(REMOVE_AT remotes 0 1) 685 | if(NOT head MATCHES "(.+);(.+)") 686 | message(FATAL_ERROR "Bad remote arguments? ${head}") 687 | endif() 688 | set(name "${CMAKE_MATCH_1}") 689 | set(url "${CMAKE_MATCH_2}") 690 | _pmm_conan_norm_url_var(url) 691 | set(verify_ssl True) 692 | if(name MATCHES "(.+)(::no_verify)") 693 | set(verify_ssl False) 694 | set(name "${CMAKE_MATCH_1}") 695 | endif() 696 | if(NOT (url IN_LIST all_urls)) 697 | _pmm_log("Add Conan remote '${name}' for ${url}") 698 | _pmm_exec("${PMM_CONAN_EXECUTABLE}" remote add "${name}" "${url}" "${verify_ssl}" --force) 699 | if(_PMM_RC) 700 | message(FATAL_ERROR "Failed to add Conan remote ${name} [${_PMM_RC}]: ${_PMM_OUTPUT}") 701 | endif() 702 | endif() 703 | endwhile() 704 | endfunction() 705 | 706 | # Implement the `CONAN` subcommand 707 | function(_pmm_conan) 708 | _pmm_parse_args( 709 | . BINCRAFTERS COMMUNITY 710 | - BUILD 711 | + SETTINGS OPTIONS ENV REMOTES INSTALL_DEPENDS 712 | ) 713 | 714 | get_cmake_property(__was_setup _PMM_CONAN_WAS_SETUP) 715 | if(__was_setup) 716 | _pmm_log(WARNING "pmm(CONAN) ran more than once during configure. This is not supported.") 717 | endif() 718 | 719 | _pmm_conan_vars() 720 | 721 | if(ARG_BINCRAFTERS) 722 | list(APPEND ARG_REMOTES bincrafters https://bincrafters.jfrog.io/artifactory/api/conan/public-conan) 723 | endif() 724 | if(ARG_COMMUNITY) 725 | message(AUTHOR_WARNING "The conan-community repository is deprecated. Requesting it has no effect.") 726 | endif() 727 | 728 | # Ensure that we have Conan 729 | _pmm_ensure_conan() 730 | if(NOT PMM_CONAN_EXECUTABLE) 731 | message(SEND_ERROR "Cannot use Conan with PMM because we were unable to find/obtain a Conan executable.") 732 | return() 733 | endif() 734 | if(NOT CONAN_PREV_EXE STREQUAL PMM_CONAN_EXECUTABLE) 735 | # Enter this branch if the path to the Conan executable has been changed. 736 | # Check that we are actually able to run this executable: 737 | _pmm_exec("${PMM_CONAN_EXECUTABLE}" --version) 738 | if(_PMM_RC) 739 | # We failed to run it. Drop the bad exe path from the cache and 740 | # display an error message 741 | set(exe "${PMM_CONAN_EXECUTABLE}") 742 | unset(PMM_CONAN_EXECUTABLE CACHE) 743 | message(FATAL_ERROR "Conan executable (${exe}) seems invalid [${_PMM_RC}]:\n${_PMM_OUTPUT}") 744 | endif() 745 | # Detect the Conan version 746 | set(_prev "${PMM_CONAN_VERSION}") 747 | if(_PMM_OUTPUT MATCHES "Conan version ([0-9]+\\.[0-9]+\\.[0-9]+)") 748 | set(PMM_CONAN_VERSION "${CMAKE_MATCH_1}" CACHE INTERNAL "Conan version") 749 | if(PMM_CONAN_VERSION VERSION_GREATER "2") 750 | _pmm_log(WARNING "PMM does not yet support Conan 2+") 751 | endif() 752 | if(NOT _prev) 753 | _pmm_log("Conan version: ${PMM_CONAN_VERSION}") 754 | endif() 755 | else() 756 | _pmm_log(WARNING "Command (${PMM_CONAN_EXECUTABLE} --version) did not produce parseable output:\n${_PMM_OUTPUT}") 757 | set(PMM_CONAN_VERSION "Unknown" CACHE INTERNAL "Conan version") 758 | endif() 759 | endif() 760 | # Keep track of what exe we just found 761 | set(CONAN_PREV_EXE "${PMM_CONAN_EXECUTABLE}" CACHE INTERNAL "Previous known-good Conan executable" FORCE) 762 | _pmm_generate_shim(conan "${PMM_CONAN_EXECUTABLE}") 763 | 764 | # Find the conanfile for the project 765 | unset(conanfile) 766 | foreach(fname IN ITEMS conanfile.txt conanfile.py) 767 | set(cand "${PROJECT_SOURCE_DIR}/${fname}") 768 | if(EXISTS "${cand}") 769 | set(conanfile "${cand}") 770 | endif() 771 | endforeach() 772 | 773 | # Enable the remote repositories that the user may want to use 774 | _pmm_conan_ensure_remotes("${ARG_REMOTES}") 775 | 776 | # Check that there is a Conanfile, or we might be otherwise building in the 777 | # local cache. 778 | if(NOT DEFINED conanfile AND NOT CONAN_EXPORTED) 779 | message(FATAL_ERROR "pf(CONAN) requires a Conanfile in your project source directory") 780 | endif() 781 | # Go! 782 | set(__conanfile "${conanfile}") 783 | set(__install_depends "${ARG_INSTALL_DEPENDS}") 784 | _pmm_conan_install() 785 | # Lift these env vars so that they are visible after pmm() returns 786 | _pmm_lift(CMAKE_MODULE_PATH) 787 | _pmm_lift(CMAKE_PREFIX_PATH) 788 | # Mark that we successfully ran Conan 789 | set_property(GLOBAL PROPERTY _PMM_CONAN_WAS_SETUP TRUE) 790 | endfunction() 791 | 792 | 793 | function(_pmm_conan_gen_profile destpath be_lazy) 794 | if(EXISTS "${destpath}" AND be_lazy) 795 | return() 796 | endif() 797 | set(tmpdir "${PMM_DIR}/_gen-profile-project") 798 | set(tmpdir_build "${tmpdir}/_build") 799 | file(REMOVE_RECURSE "${tmpdir}") 800 | file(REMOVE_RECURSE "${tmpdir_build}") 801 | # Detect if we have Ninja 802 | find_program(_ninja_exe NAMES ninja-build ninja) 803 | # Generate a small project 804 | file(MAKE_DIRECTORY "${tmpdir}") 805 | string(CONFIGURE [[ 806 | cmake_minimum_required(VERSION 3.13.0) 807 | project(Dummy) 808 | set(PMM_DIR "@PMM_DIR@") 809 | include("@CMAKE_SCRIPT_MODE_FILE@") 810 | pmm(CONAN) 811 | ]] cml @ONLY) 812 | file(WRITE "${tmpdir}/conanfile.txt" "") 813 | file(WRITE "${tmpdir}/CMakeLists.txt" "${cml}") 814 | set(more_args) 815 | if(_ninja_exe) 816 | list(APPEND more_args "-GNinja") 817 | endif() 818 | # Configure the project 819 | _pmm_log("Generating Conan profile ...") 820 | execute_process( 821 | COMMAND "${CMAKE_COMMAND}" "-H${tmpdir}" "-B${tmpdir_build}" ${more_args} 822 | RESULT_VARIABLE retc 823 | OUTPUT_VARIABLE out 824 | ERROR_VARIABLE out 825 | ) 826 | if(retc) 827 | message(FATAL_ERROR "Failed to configure project to generate Conan profile [${retc}]:\n${out}") 828 | endif() 829 | file(GLOB profile_file "${tmpdir_build}/pmm-conan*.profile") 830 | file(RENAME "${profile_file}" "${destpath}") 831 | _pmm_log("Conan profile written to file: ${destpath}") 832 | endfunction() 833 | 834 | 835 | function(_pmm_print_conan_where cookie) 836 | message("${cookie}${PMM_CONAN_EXECUTABLE}") 837 | endfunction() 838 | 839 | 840 | function(_pmm_script_main_conan) 841 | _pmm_parse_args( 842 | -hardcheck 843 | . 844 | /NotManaged 845 | /Version 846 | /Create 847 | /Upload 848 | /All 849 | /NoOverwrite 850 | /Clean 851 | /Export 852 | /Install 853 | /Uninstall 854 | /GenProfile 855 | /Lazy # For /GenProfile 856 | /Upgrade # [deprecated] 857 | - /Ref /Remote /Profile /Where 858 | + /Settings /Options /BuildPolicy /EnsureRemotes 859 | ) 860 | 861 | _pmm_conan_vars() 862 | 863 | if(ARG_/Upgrade) 864 | _pmm_log("The /Upgrade option is deprecated and has no effect.") 865 | endif() 866 | 867 | set(PMM_CONAN_MANAGED_NO_INSTALL TRUE) 868 | 869 | if(ARG_/Uninstall) 870 | _pmm_conan_uninstall() 871 | endif() 872 | 873 | if(ARG_/Install) 874 | set(PMM_CONAN_MANAGED_NO_INSTALL FALSE) 875 | _pmm_ensure_conan() 876 | if(NOT PMM_CONAN_EXECUTABLE) 877 | message(FATAL_ERROR "Failed to install a Conan executable") 878 | endif() 879 | endif() 880 | 881 | if(ARG_/NotManaged) 882 | set(PMM_CONAN_MANAGED FALSE) 883 | else() 884 | set(PMM_CONAN_MANAGED TRUE) 885 | endif() 886 | 887 | if(DEFINED ARG_/Where) 888 | _pmm_ensure_conan() 889 | if(NOT PMM_CONAN_EXECUTABLE) 890 | message(FATAL_ERROR "/Where may only be used after Conan has been installed. Try passing /Install") 891 | endif() 892 | _pmm_print_conan_where("${ARG_/Where}") 893 | endif() 894 | 895 | if(ARG_/Version) 896 | _pmm_ensure_conan() 897 | execute_process(COMMAND "${PMM_CONAN_EXECUTABLE}" --version) 898 | endif() 899 | 900 | if(ARG_/EnsureRemotes) 901 | _pmm_ensure_conan() 902 | if(NOT PMM_CONAN_EXECUTABLE) 903 | message(FATAL_ERROR "/EnsureRemotes may only be used after Conan has been installed. Try passing /Install") 904 | endif() 905 | _pmm_conan_ensure_remotes("${ARG_/EnsureRemotes}") 906 | endif() 907 | 908 | if(ARG_/Create AND ARG_/Export) 909 | message(FATAL_ERROR "/Export and /Create can not be specified together") 910 | endif() 911 | 912 | if(ARG_/GenProfile) 913 | if(NOT ARG_/Profile) 914 | message(FATAL_ERROR "Specify `/Profile ` when using /GenProfile") 915 | endif() 916 | get_filename_component(pr_dest "${ARG_/Profile}" ABSOLUTE) 917 | _pmm_conan_gen_profile("${pr_dest}" "${ARG_/Lazy}") 918 | endif() 919 | 920 | set(profile_args) 921 | if(ARG_/Profile) 922 | set(profile_args --profile "${ARG_/Profile}") 923 | endif() 924 | set(settings_args) 925 | foreach(s IN LISTS ARG_/Settings) 926 | list(APPEND settings_args --settings "${s}") 927 | endforeach() 928 | set(options_args) 929 | foreach(o IN LISTS ARG_/Options) 930 | list(APPEND options_args --options "${o}") 931 | endforeach() 932 | # All args used for package install/create/info etc.: 933 | set(all_config_args ${profile_args} ${settings_args} ${options_args}) 934 | 935 | set(create_args ${all_config_args}) 936 | 937 | if(ARG_/Create) 938 | if(NOT ARG_/Ref) 939 | message(FATAL_ERROR "Pass a /Ref for /Create") 940 | endif() 941 | _pmm_ensure_conan() 942 | foreach(policy IN LISTS ARG_/BuildPolicy) 943 | list(APPEND create_args "--build=${policy}") 944 | endforeach() 945 | execute_process( 946 | COMMAND "${PMM_CONAN_EXECUTABLE}" create ${create_args} "${CMAKE_SOURCE_DIR}" "${ARG_/Ref}" 947 | RESULT_VARIABLE retc 948 | ) 949 | if(retc) 950 | message(FATAL_ERROR "Create failed [${retc}]") 951 | endif() 952 | endif() 953 | 954 | if(ARG_/Export) 955 | if(NOT ARG_/Ref) 956 | message(FATAL_ERROR "Pass /Ref when for /Export") 957 | endif() 958 | _pmm_ensure_conan() 959 | execute_process( 960 | COMMAND "${PMM_CONAN_EXECUTABLE}" export "${CMAKE_SOURCE_DIR}" "${ARG_/Ref}" 961 | RESULT_VARIABLE retc 962 | ) 963 | if(retc) 964 | message(FATAL_ERROR "Export failed [${retc}]") 965 | endif() 966 | endif() 967 | 968 | if(ARG_/Clean) 969 | _pmm_ensure_conan() 970 | execute_process(COMMAND "${PMM_CONAN_EXECUTABLE}" remove * -fsb) 971 | endif() 972 | 973 | if(ARG_/Upload) 974 | _pmm_ensure_conan() 975 | if(ARG_/Ref MATCHES ".+@.+") 976 | set(full_ref "${ARG_/Ref}") 977 | else() 978 | _pmm_exec("${PMM_CONAN_EXECUTABLE}" info ${all_config_args} "${CMAKE_SOURCE_DIR}") 979 | if(_PMM_RC) 980 | message(FATAL_ERROR "Failed to get package info [${_PMM_RC}]:\n${_PMM_OUTPUT}") 981 | endif() 982 | if(NOT _PMM_OUTPUT MATCHES "([^\n]+)@PROJECT.*") 983 | if(NOT _PMM_OUTPUT MATCHES "conanfile[^\n]+ \\(([^\n]+)@None/None") 984 | message(FATAL_ERROR "Can't parse Conan output [${_PMM_RC}]:\n${_PMM_OUTPUT}") 985 | endif() 986 | endif() 987 | set(full_ref "${CMAKE_MATCH_1}@${ARG_/Ref}") 988 | endif() 989 | set(cmd "${PMM_CONAN_EXECUTABLE}" upload --confirm --check) 990 | if(ARG_/Remote) 991 | list(APPEND cmd --remote "${ARG_/Remote}") 992 | endif() 993 | if(ARG_/All) 994 | list(APPEND cmd --all) 995 | endif() 996 | if(ARG_/NoOverwrite) 997 | list(APPEND cmd --no-overwrite all) 998 | endif() 999 | list(APPEND cmd "${full_ref}") 1000 | execute_process( 1001 | COMMAND ${cmd} 1002 | RESULT_VARIABLE retc 1003 | ) 1004 | if(retc) 1005 | message(FATAL_ERROR "Upload failed [${retc}]") 1006 | endif() 1007 | endif() 1008 | endfunction() 1009 | -------------------------------------------------------------------------------- /pmm/dds.cmake: -------------------------------------------------------------------------------- 1 | # Regular include_guard() doesn't work because this file may be downloaded and 2 | # replaced within a single CMake config run. 3 | get_cmake_property(_was_included _PMM_DDS_CMAKE_INCLUDED) 4 | if(_was_included) 5 | return() 6 | endif() 7 | set_property(GLOBAL PROPERTY _PMM_DDS_INCLUDED TRUE) 8 | 9 | pmm_option(PMM_DDS_VERSION "0.1.0-alpha.6") 10 | pmm_option(PMM_DDS_URL_BASE "https://github.com/vector-of-bool/dds/releases/download/${PMM_DDS_VERSION}") 11 | 12 | if(NOT CMAKE_SCRIPT_MODE_FILE) 13 | # Script-mode doesn't like calling define_property() 14 | define_property(GLOBAL PROPERTY DDS_DEPENDS 15 | BRIEF_DOCS "Dependencies for dds" 16 | FULL_DOCS "The accumulated list of dependencies that have been requested via pmm(DDS) with the DEPENDS argument" 17 | ) 18 | define_property(GLOBAL PROPERTY DDS_DEP_FILES 19 | BRIEF_DOCS "Dependency files for dds" 20 | FULL_DOCS "The accumulated list of dependency JSON5 files that have been requested via pmm(DDS) with the DEP_FILES argument" 21 | ) 22 | endif() 23 | 24 | set_property(GLOBAL PROPERTY DDS_DEPENDS "") 25 | set_property(GLOBAL PROPERTY DDS_DEP_FILES "") 26 | 27 | function(_pmm_get_dds_exe out) 28 | if(DEFINED PMM_DDS_EXECUTABLE) 29 | _pmm_log("Using user-specified DDS executable: ${PMM_DDS_EXECUTABLE}") 30 | set("${out}" "${PMM_DDS_EXECUTABLE}" PARENT_SCOPE) 31 | return() 32 | endif() 33 | get_cmake_property(dds_exe _PMM_DDS_EXE) 34 | if(dds_exe) 35 | set("${out}" "${dds_exe}" PARENT_SCOPE) 36 | return() 37 | endif() 38 | set(sysname "${CMAKE_HOST_SYSTEM_NAME}") 39 | if(sysname MATCHES "^Windows") 40 | set(dds_dest "${PMM_DIR}/dds.exe") 41 | set(dds_fname "dds-win-x64.exe") 42 | elseif(sysname STREQUAL "Linux") 43 | set(dds_dest "${PMM_DIR}/dds") 44 | set(dds_fname "dds-linux-x64") 45 | elseif(sysname STREQUAL "Darwin") 46 | set(dds_dest "${PMM_DIR}/dds") 47 | set(dds_fname "dds-macos-x64") 48 | elseif(sysname STREQUAL "FreeBSD") 49 | set(dds_dest "${PMM_DIR}/dds") 50 | set(dds_fname "dds-freebsd-x64") 51 | else() 52 | message(FATAL_ERROR "We are unnable to automatically download a DDS executable for this system.") 53 | endif() 54 | pmm_option(PMM_DDS_FILENAME "${dds_fname}") 55 | pmm_option(PMM_DDS_URL "${PMM_DDS_URL_BASE}/${PMM_DDS_FILENAME}") 56 | if(NOT EXISTS "${dds_dest}") 57 | # Download to a temporary location 58 | set(dds_tempfile "${PMM_DIR}/tmp") 59 | get_filename_component(dds_fname "${dds_dest}" NAME) 60 | set(dds_tempfile "${dds_tempfile}/${dds_fname}") 61 | _pmm_log(VERBOSE "Downloading DDS from ${dds_url}") 62 | _pmm_download("${PMM_DDS_URL}" "${dds_tempfile}") 63 | # Copy the file to its destination with the execute permission bits 64 | get_filename_component(dds_dest_dir "${dds_dest}" DIRECTORY) 65 | file( 66 | COPY "${dds_tempfile}" 67 | DESTINATION "${dds_dest_dir}" 68 | FILE_PERMISSIONS 69 | OWNER_READ OWNER_WRITE OWNER_EXECUTE 70 | GROUP_READ GROUP_EXECUTE 71 | WORLD_READ WORLD_EXECUTE 72 | ) 73 | endif() 74 | set_property(GLOBAL PROPERTY _PMM_DDS_EXE "${dds_dest}") 75 | _pmm_log(DEBUG "Local DDS executable: ${dds_dest}") 76 | set("${out}" "${dds_dest}" PARENT_SCOPE) 77 | endfunction() 78 | 79 | function(_pmm_dds_json5_flags_array out) 80 | set(flags_arr) 81 | foreach(flag IN LISTS ARGN) 82 | string(GENEX_STRIP "${flag}" stripped) 83 | if(NOT stripped STREQUAL flag) 84 | # The option contained a generator expression. We aren't able to handle those yet... 85 | # This will be especially useful with multiconf builds 86 | _pmm_log(WARNING "'dds' toolchain will not include compiler option/definition containing a generator expression: ${flag}") 87 | continue() 88 | endif() 89 | string(REPLACE "'" "\\'" flag "${flag}") 90 | string(APPEND flags_arr "'${flag}', ") 91 | endforeach() 92 | set("${out}" "[${flags_arr}]" PARENT_SCOPE) 93 | endfunction() 94 | 95 | function(_pmm_dds_generate_toolchain out) 96 | get_filename_component(toolchain_dest "${PMM_DIR}/bpt-toolchain.json5" ABSOLUTE) 97 | 98 | # First, determine the compiler_id 99 | if(DEFINED CMAKE_CXX_COMPILER_ID) 100 | set(comp_id "${CMAKE_CXX_COMPILER_ID}") 101 | elseif(DEFINED CMAKE_C_COMPILER_ID) 102 | set(comp_id "${CMAKE_C_COMPILER_ID}") 103 | else() 104 | message(FATAL_ERROR "We couldn't determine the compiler ID. Are the C and C++ languages enabled?") 105 | endif() 106 | 107 | # Check that we recognize the compiler ID 108 | if(NOT comp_id MATCHES "^(AppleClang|Clang|GNU|MSVC)$") 109 | _pmm_log(WARNING "We don't recognize the compiler ID '${comp_id}'") 110 | _pmm_log(WARNING "It is likely that you will need to write your own toolchain file by hand...") 111 | endif() 112 | 113 | string(TOLOWER "${comp_id}" comp_id) 114 | 115 | # Determine the cxx_version to pass through the toolchain 116 | get_directory_property(std CXX_STANDARD) 117 | if(NOT std) 118 | set(std "${CMAKE_CXX_STANDARD}") 119 | endif() 120 | if(std EQUAL 98) 121 | set(cxx_version_line "cxx_version: 'c++98',") 122 | elseif(std EQUAL 11) 123 | set(cxx_version_line "cxx_version: 'c++11',") 124 | elseif(std EQUAL 14) 125 | set(cxx_version_line "cxx_version: 'c++14',") 126 | elseif(std EQUAL 17) 127 | set(cxx_version_line "cxx_version: 'c++17',") 128 | elseif(std EQUAL 20) 129 | set(cxx_version_line "cxx_version: 'c++20',") 130 | elseif(std) 131 | _pmm_log(WARNING "We don't recognize the CXX_STANDARD version '${std}'.") 132 | _pmm_log(WARNING "You may want to specify a standard version when calling pmm()") 133 | else() 134 | # No standard was set. We'll leave it unspecified and allow the compiler's default 135 | endif() 136 | 137 | set(c_compiler_line) 138 | if(CMAKE_C_COMPILER) 139 | set(c_compiler_line "c_compiler: '${CMAKE_C_COMPILER}',") 140 | endif() 141 | 142 | set(cxx_compiler_line) 143 | if(CMAKE_CXX_COMPILER) 144 | set(cxx_compiler_line "cxx_compiler: '${CMAKE_CXX_COMPILER}',") 145 | endif() 146 | 147 | # Multi-conf builds are *possible*, but will require a significant amount of 148 | # work. 149 | if(CMAKE_CONFIGURATION_TYPES) 150 | _pmm_log(WARNING "Using pmm+bpt with multi-conf builds is not yet fully supported.") 151 | set(gen_debug TRUE) 152 | set(optimize true) 153 | endif() 154 | 155 | set(rt_option "${CMAKE_MSVC_RUNTIME_LIBRARY}") 156 | if(NOT rt_option) 157 | set(rt_option "MultiThreaded$<$:Debug>DLL") 158 | endif() 159 | 160 | # Enable debug info: 161 | if(CMAKE_BUILD_TYPE MATCHES "^(Debug|RelWithDebInfo|)$") 162 | set(gen_debug TRUE) 163 | string(REPLACE "$" 1 rt_option "${rt_option}") 164 | else() 165 | set(gen_debug FALSE) 166 | string(REPLACE "$" 0 rt_option "${rt_option}") 167 | endif() 168 | 169 | # Enable optimizations: 170 | if(CMAKE_BUILD_TYPE MATCHES "^(Release|RelWithDebInfo|MinSizeRel)$") 171 | set(optimize true) 172 | else() 173 | set(optimize false) 174 | endif() 175 | 176 | # Determine the runtime options 177 | string(REPLACE "$<1:Debug>" Debug rt_option "${rt_option}") 178 | string(REPLACE "$<0:Debug>" "" rt_option "${rt_option}") 179 | set(rt_obj "{}") 180 | if(MSVC) 181 | set(rt_debug false) 182 | if(rt_option MATCHES "^MultiThreadedDebug") 183 | set(rt_debug true) 184 | endif() 185 | set(rt_static true) 186 | if(rt_option MATCHES "DLL$") 187 | set(rt_static false) 188 | endif() 189 | set(rt_obj "{debug: ${rt_debug}, static: ${rt_static}}") 190 | endif() 191 | 192 | # Pass in language flags for the current config 193 | string(TOUPPER "${CMAKE_BUILD_TYPE}" bt_upper) 194 | set(c_bt_flags "${CMAKE_C_FLAGS} ${CMAKE_C_FLAGS_${bt_upper}}") 195 | set(cxx_bt_flags "${CMAKE_CXX_FLAGS} ${CMAKE_CXX_FLAGS_${bt_upper}}") 196 | separate_arguments(c_opts NATIVE_COMMAND "${c_bt_flags}") 197 | separate_arguments(cxx_opts NATIVE_COMMAND "${cxx_bt_flags}") 198 | _pmm_dds_json5_flags_array(c_flags_arr ${c_opts}) 199 | _pmm_dds_json5_flags_array(cxx_flags_arr ${cxx_opts}) 200 | 201 | # Pass thru compile flags and definitions from the enclosing source directory 202 | get_directory_property(compile_flags COMPILE_OPTIONS) 203 | get_directory_property(defs COMPILE_DEFINITIONS) 204 | foreach(def IN LISTS defs) 205 | list(APPEND compile_flags "-D${def}") 206 | endforeach() 207 | _pmm_dds_json5_flags_array(flags_arr ${compile_flags}) 208 | 209 | if(NOT gen_debug) 210 | set(debug_str "none") 211 | elseif(MSVC AND ("/Zi" IN_LIST cxx_opts 212 | OR "/ZI" IN_LIST cxx_opts)) 213 | # Set flags for doing split debug info 214 | set(debug_str "split") 215 | elseif("/Z7" IN_LIST cxx_opts) 216 | # Don't pass an additional /Z7 217 | set(debug_str "none") 218 | else() 219 | set(debug_str "embedded") 220 | endif() 221 | 222 | # Detect a compiler launcher (i.e. `ccache`) 223 | if(DEFINED PMM_DDS_COMPILER_LAUNCHER OR DEFINED PMM_BPT_COMPILER_LAUNCHER) 224 | set(launcher "${PMM_DDS_COMPILER_LAUNCHER}${PMM_BPT_COMPILER_LAUNCHER}") 225 | else() 226 | get_directory_property(launcher CXX_COMPILER_LAUNCHER) 227 | if(NOT launcher) 228 | set(launcher "${CMAKE_CXX_COMPILER_LAUNCHER}") 229 | endif() 230 | endif() 231 | _pmm_dds_json5_flags_array(launcher ${launcher}) 232 | 233 | string(CONFIGURE [[ 234 | { 235 | compiler_id: '@comp_id@', 236 | @c_compiler_line@ 237 | @cxx_compiler_line@ 238 | @cxx_version_line@ 239 | flags: @flags_arr@, 240 | c_flags: @c_flags_arr@, 241 | cxx_flags: @cxx_flags_arr@, 242 | debug: '@debug_str@', 243 | optimize: @optimize@, 244 | runtime: @rt_obj@, 245 | compiler_launcher: @launcher@, 246 | } 247 | ]] toolchain_content @ONLY) 248 | file(WRITE "${toolchain_dest}" "${toolchain_content}") 249 | set("${out}" "${toolchain_dest}" PARENT_SCOPE) 250 | _pmm_log(DEBUG "Generated bpt toolchain at ${toolchain_dest} with content: ${toolchain_content}") 251 | endfunction() 252 | 253 | function(_pmm_dds) 254 | _pmm_log(WARNING "The DDS subcommand is being replaced with BPT! Update soon!") 255 | _pmm_parse_args( 256 | -hardcheck 257 | - TOOLCHAIN 258 | + DEP_FILES DEPENDS 259 | ) 260 | 261 | _pmm_get_dds_exe(dds_exe) 262 | 263 | _pmm_generate_shim(dds "${dds_exe}") 264 | 265 | # The user may call pmm(DDS) multiple times, in which case we append to the 266 | # dependencies as we import them, rather than replacing the libman index 267 | # with the new set of dependencies. 268 | set_property(GLOBAL APPEND PROPERTY DDS_DEPENDS ${ARG_DEPENDS}) 269 | set_property(GLOBAL APPEND PROPERTY DDS_DEP_FILES ${ARG_DEP_FILES}) 270 | 271 | # Get the total accumulated set of dependencies/dep-files 272 | get_cmake_property(acc_depends DDS_DEPENDS) 273 | get_cmake_property(acc_dep_files DDS_DEP_FILES) 274 | 275 | # Build the command-line arguments to use with build-deps 276 | set(bdeps_args ${acc_depends}) 277 | foreach(fname IN LISTS acc_dep_files) 278 | get_filename_component(deps_fpath "${fname}" ABSOLUTE) 279 | list(APPEND bdeps_args "--deps-file=${deps_fpath}") 280 | endforeach() 281 | 282 | if(NOT ARG_TOOLCHAIN) 283 | # If the user didn't specify a toolchain, generate one now based on the 284 | # CMake environment 285 | _pmm_dds_generate_toolchain(ARG_TOOLCHAIN) 286 | endif() 287 | 288 | set(inc_file "${PROJECT_BINARY_DIR}/_dds-deps.cmake") 289 | list(APPEND bdeps_args "--cmake=${inc_file}") 290 | list(APPEND bdeps_args "--toolchain=${ARG_TOOLCHAIN}") 291 | 292 | _pmm_exec( 293 | "${dds_exe}" build-deps ${bdeps_args} 294 | NO_EAT_OUTPUT 295 | WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" 296 | ) 297 | if(_PMM_RC) 298 | message(FATAL_ERROR "DDS failed to build our dependencies [${_PMM_RC}]") 299 | endif() 300 | include("${inc_file}") 301 | endfunction() 302 | -------------------------------------------------------------------------------- /pmm/entry.cmake: -------------------------------------------------------------------------------- 1 | # Set the policies for the range of supported CMake versions 2 | cmake_policy(PUSH) 3 | cmake_minimum_required(VERSION 3.13.0...3.21.4) 4 | 5 | if(NOT DEFINED _PMM_BOOTSTRAP_VERSION OR _PMM_BOOTSTRAP_VERSION LESS 4) 6 | message(FATAL_ERROR 7 | "Using PMM ${PMM_VERSION} requires updating the pmm.cmake bootstrap script. " 8 | "Visit the PMM repository to obtain a new copy of pmm.cmake for your project.") 9 | endif() 10 | 11 | # Unset variables that may be affected by a version change 12 | if(NOT PMM_VERSION STREQUAL PMM_PRIOR_VERSION) 13 | foreach(var IN ITEMS PMM_CONAN_EXECUTABLE) 14 | unset(${var} CACHE) 15 | endforeach() 16 | endif() 17 | set(PMM_PRIOR_VERSION "${PMM_VERSION}" CACHE STRING "Previous version of PMM in the source tree" FORCE) 18 | 19 | function(_pmm_log arg) 20 | string(REPLACE ";" " " msg "${ARGN}") 21 | if(arg STREQUAL DEBUG) 22 | if(PMM_DEBUG) 23 | message(STATUS "[pmm] [debug ] ${ARGN}") 24 | endif() 25 | elseif(arg STREQUAL VERBOSE) 26 | if(PMM_DEBUG OR PMM_VERBOSE) 27 | message(STATUS "[pmm] [verbose] ${ARGN}") 28 | endif() 29 | elseif(arg STREQUAL WARNING) 30 | message(STATUS "[pmm] [warn ] ${ARGN}") 31 | else() 32 | message(STATUS "[pmm] ${ARGV}") 33 | endif() 34 | endfunction() 35 | 36 | function(_pmm_download url dest) 37 | cmake_parse_arguments(ARG "NO_CHECK" "RESULT_VARIABLE" "" "${ARGN}") 38 | set(tmp "${dest}.tmp") 39 | _pmm_log(DEBUG "Downloading ${url}") 40 | _pmm_log(DEBUG "File will be written to ${dest}") 41 | file( 42 | DOWNLOAD "${url}" 43 | "${tmp}" 44 | STATUS st 45 | ) 46 | list(GET st 0 rc) 47 | list(GET st 1 msg) 48 | _pmm_log(DEBUG "Download status [${rc}]: ${msg}") 49 | if(rc) 50 | file(REMOVE "${tmp}") 51 | if(NOT ARG_NO_CHECK) 52 | message(FATAL_ERROR "Error while downloading file from '${url}' to '${dest}' [${rc}]: ${msg}") 53 | endif() 54 | _pmm_log(VERBOSE "Failed to download file ${url}: [${rc}]: ${msg}") 55 | if(ARG_RESULT_VARIABLE) 56 | set("${ARG_RESULT_VARIABLE}" FALSE PARENT_SCOPE) 57 | endif() 58 | else() 59 | file(RENAME "${tmp}" "${dest}") 60 | _pmm_log(VERBOSE "Downloaded file ${url} to ${dest}") 61 | if(ARG_RESULT_VARIABLE) 62 | set("${ARG_RESULT_VARIABLE}" TRUE PARENT_SCOPE) 63 | endif() 64 | endif() 65 | endfunction() 66 | 67 | macro(_pmm_check_and_include_file filename) 68 | get_filename_component(_dest "${PMM_DIR}/${filename}" ABSOLUTE) 69 | if(NOT EXISTS "${_dest}" OR PMM_ALWAYS_DOWNLOAD) 70 | _pmm_download("${PMM_URL}/${filename}" "${_dest}") 71 | endif() 72 | include("${_dest}") 73 | endmacro() 74 | 75 | # Download the required modules 76 | _pmm_check_and_include_file(util.cmake) 77 | _pmm_check_and_include_file(main.cmake) 78 | 79 | # Do the update check. 80 | function(_pmm_check_updates) 81 | set(_latest_info_url "${PMM_URL_BASE}/latest-info.cmake") 82 | set(_latest_info_file "${PMM_DIR}/latest-info.cmake") 83 | file(DOWNLOAD "${_latest_info_url}" "${_latest_info_file}" STATUS did_download TIMEOUT 5) 84 | list(GET did_download 0 rc) 85 | if(rc EQUAL 0) 86 | include("${_latest_info_file}") 87 | _pmm_lift(PMM_LATEST_VERSION) 88 | else() 89 | if(NOT PMM_IGNORE_NEW_VERSION) 90 | _pmm_log("Failed to check for updates (Couldn't download ${_latest_info_url})") 91 | endif() 92 | endif() 93 | endfunction() 94 | 95 | _pmm_check_updates() 96 | 97 | if(CMAKE_SCRIPT_MODE_FILE) 98 | _pmm_script_main() 99 | endif() 100 | 101 | # Restore prior policy settings before returning to our includer 102 | cmake_policy(POP) 103 | -------------------------------------------------------------------------------- /pmm/main.cmake: -------------------------------------------------------------------------------- 1 | if(NOT "$ENV{HOME}" STREQUAL "") 2 | set(_PMM_USER_HOME "$ENV{HOME}") 3 | else() 4 | set(_PMM_USER_HOME "$ENV{PROFILE}") 5 | endif() 6 | 7 | if(WIN32) 8 | set(_PMM_USER_DATA_DIR "$ENV{LocalAppData}/pmm/${PMM_VERSION}") 9 | elseif("$ENV{XDG_DATA_HOME}") 10 | set(_PMM_USER_DATA_DIR "$ENV{XDG_DATA_HOME}/pmm/${PMM_VERSION}") 11 | else() 12 | set(_PMM_USER_DATA_DIR "${_PMM_USER_HOME}/.local/share/pmm/${PMM_VERSION}") 13 | endif() 14 | 15 | # The main function. 16 | function(_pmm_project_fn) 17 | _pmm_parse_args( 18 | . DEBUG VERBOSE 19 | + CONAN VCPKG CMakeCM DDS BPT 20 | ) 21 | 22 | _pmm_generate_cli_scripts(FALSE) 23 | 24 | if(ARG_DEBUG) 25 | set(PMM_DEBUG TRUE) 26 | endif() 27 | if(ARG_VERBOSE) 28 | set(PMM_VERBOSE TRUE) 29 | endif() 30 | 31 | if(ARG_UNPARSED_ARGUMENTS) 32 | string(REPLACE ";" ", " unknown "${ARG_UNPARSED_ARGUMENTS}") 33 | message(FATAL_ERROR 34 | "Unrecognzed arguments: '${unknown}' " 35 | "(Check argument spelling and order)") 36 | endif() 37 | 38 | if(DEFINED ARG_CONAN OR "CONAN" IN_LIST ARGV) 39 | _pmm_check_and_include_file(python.cmake) 40 | _pmm_check_and_include_file(conan.cmake) 41 | _pmm_conan(${ARG_CONAN}) 42 | _pmm_lift(CMAKE_MODULE_PATH CMAKE_PREFIX_PATH) 43 | endif() 44 | if(DEFINED ARG_VCPKG OR "VCPKG" IN_LIST ARGV) 45 | _pmm_check_and_include_file(vcpkg.cmake) 46 | _pmm_vcpkg(${ARG_VCPKG}) 47 | endif() 48 | if(DEFINED ARG_CMakeCM OR "CMakeCM" IN_LIST ARGV) 49 | _pmm_check_and_include_file(cmcm.cmake) 50 | _pmm_cmcm(${ARG_CMakeCM}) 51 | _pmm_lift(CMAKE_MODULE_PATH) 52 | endif() 53 | if(DEFINED ARG_DDS OR "DDS" IN_LIST ARGV) 54 | _pmm_check_and_include_file(dds.cmake) 55 | _pmm_dds(${ARG_DDS}) 56 | endif() 57 | if(DEFINED ARG_BPT OR "BPT" IN_LIST ARGV) 58 | _pmm_check_and_include_file(bpt.cmake) 59 | _pmm_bpt(${ARG_BPT}) 60 | endif() 61 | _pmm_lift(_PMM_INCLUDE) 62 | endfunction() 63 | 64 | macro(pmm) 65 | unset(_PMM_INCLUDE) 66 | _pmm_project_fn(${ARGV}) 67 | foreach(inc IN LISTS _PMM_INCLUDE) 68 | include(${inc}) 69 | endforeach() 70 | endmacro() 71 | 72 | function(_pmm_script_main) 73 | _pmm_parse_script_args( 74 | -nocheck 75 | . /Conan /Help /Debug /Verbose 76 | ) 77 | 78 | if(ARG_/Help) 79 | show_cli_help() 80 | return() 81 | endif() 82 | 83 | if(ARG_/Debug) 84 | set(PMM_DEBUG TRUE) 85 | endif() 86 | if(ARG_/Verbose) 87 | set(PMM_VERBOSE TRUE) 88 | endif() 89 | 90 | if(ARG_/Conan) 91 | _pmm_check_and_include_file(python.cmake) 92 | _pmm_check_and_include_file(conan.cmake) 93 | _pmm_script_main_conan(${ARG_UNPARSED_ARGUMENTS}) 94 | else() 95 | message(ERROR "PMM did not recognise the given argument list") 96 | show_cli_help() 97 | endif() 98 | endfunction() 99 | 100 | 101 | function(show_cli_help) 102 | message([===[ 103 | Available options: 104 | 105 | /Help 106 | Display this help message 107 | 108 | /GenerateShellScript 109 | (Re)generate a pmm-cli.sh and pmm-cli.bat script for calling PMM helper commands 110 | 111 | /Conan 112 | Perform a Conan action. None of the actions are mutually exclusive, and 113 | they will be evaluated in the order they are listed below. 114 | 115 | For example, you may specify both `/Create` and `/Upload` to perform 116 | a build and an upload in a single go. 117 | 118 | Most of the actions require a Conan installation be present, and will not 119 | install it on-the-fly themselves. Add `/Install` to the command line to 120 | make sure that Conan is present. 121 | 122 | /NotManaged 123 | Do not use a PMM-managed Conan installation, and instead use one that 124 | is already installed in the environment. 125 | 126 | /Version 127 | Print the Conan version that PMM is using. 128 | 129 | /Uninstall 130 | Remove the managed Conan installation that PMM may have created. 131 | 132 | /Install 133 | Ensure that the managed Conan executable is installed. If another 134 | task is run and Conan has not already been installed, that task will 135 | fail. /Install installs lazily and is a no-op if the managed Conan is 136 | already installed. 137 | 138 | /Where 139 | Print the path to the Conan executable with the given 140 | prepended to the path on the same line. 141 | 142 | /Clean 143 | Run `conan remove * -fsb`. 144 | 145 | Removes temporary source and build folders in the local conan cache. 146 | 147 | /EnsureRemotes [[::no_verify] [...]] 148 | Ensure the given Conan remotes are defined. 149 | 150 | /Profile 151 | Specify the path to a Conan profile. Used with /GenProfile and /Create. 152 | 153 | It must either already exist, or you may pass `/GenProfile` to create it 154 | on-the-fly. 155 | 156 | /GenProfile [/Lazy] 157 | Use PMM's Conan-profile generation to write a profile file to the 158 | path specified by the /Profile argument using the current environment 159 | to configure a simple CMake project. 160 | 161 | If `/Lazy` is specified, performs a no-op if the file already exists. 162 | 163 | This can be combined with `/Create` to generate a profile on-the-fly 164 | for `conan create` to use. 165 | 166 | /Ref 167 | Specify a ``. Required for `/Export`, `/Create`, and `/Upload`. 168 | 169 | /Export 170 | Run `conan export . `. 171 | 172 | With `/Upload`, will also upload the package after export. 173 | 174 | /Create [/Settings ...] 175 | [/Options ...] 176 | [/BuildPolicy [ ...]]] 177 | Run `conan create . `. 178 | 179 | `/Settings` will add `--settings` arguments to the Conan command line, 180 | and `/Options` will add `--options` arguments to the Conan command line. 181 | 182 | `/BuildPolicy` will specify `--build` arguments to the create command. 183 | 184 | Use `/Profile` to specify a profile to be used by `conan create`. 185 | 186 | /Upload [/Remote ] [/All] [/NoOverwrite] 187 | Upload the current package (should have already been exported). 188 | 189 | `` may be a partial `user/channel` reference. In this case the full 190 | ref will be obtained using the project in the current directory. 191 | ]===]) 192 | return() 193 | endfunction() 194 | -------------------------------------------------------------------------------- /pmm/python.cmake: -------------------------------------------------------------------------------- 1 | function(_pmm_find_py_py3_launcher ovar) 2 | set("${ovar}" py-NOTFOUND PARENT_SCOPE) 3 | find_program(_ret "py") 4 | set(py "${_ret}") 5 | unset(_ret CACHE) 6 | if(NOT py) 7 | return() 8 | endif() 9 | set(versions 3.12 3.11 3.10 3.9 3.8 3.7 3.6 3.5) 10 | foreach(version IN LISTS versions) 11 | execute_process( 12 | COMMAND "${py}" "-${version}" "-m" "this" 13 | OUTPUT_VARIABLE out 14 | ERROR_VARIABLE out 15 | RESULT_VARIABLE retc 16 | ) 17 | if(NOT retc) 18 | _pmm_log("Found Python ${version} launcher: ${py} -${version}") 19 | set("${ovar}" "${py};-${version}" PARENT_SCOPE) 20 | return() 21 | endif() 22 | endforeach() 23 | endfunction() 24 | 25 | function(_pmm_find_python3 ovar) 26 | set(pyenv_root_env "$ENV{PYENV_ROOT}") 27 | set(pyenv_dirs) 28 | if(pyenv_root_env) 29 | file(GLOB pyenv_dirs "${pyenv_root_env}/versions/3.*/") 30 | else() 31 | file(GLOB pyenv_dirs "$ENV{HOME}/.pyenv/versions/3.*/") 32 | endif() 33 | list(SORT pyenv_dirs COMPARE FILE_BASENAME ORDER DESCENDING) 34 | file(GLOB c_python_dirs "C:/Python3*") 35 | _pmm_find_py_py3_launcher(py) 36 | if(py) 37 | set("${ovar}" "${py}" PARENT_SCOPE) 38 | return() 39 | endif() 40 | find_program( 41 | _ret 42 | NAMES 43 | python3.8 44 | python3.7 45 | python3.6 46 | python3.5 47 | python3.4 48 | python3.3 49 | python3.2 50 | python3.1 51 | python3.0 52 | python3 53 | python 54 | HINTS 55 | ${pyenv_dirs} 56 | ${c_python_dirs} 57 | PATH_SUFFIXES 58 | bin 59 | Scripts 60 | NAMES_PER_DIR 61 | ) 62 | if(_ret) 63 | execute_process(COMMAND "${_ret}" --version OUTPUT_VARIABLE out ERROR_VARIABLE out) 64 | if(NOT out MATCHES "^Python 3") 65 | set(_ret NOTFOUND CACHE INTERNAL "") 66 | endif() 67 | endif() 68 | set("${ovar}" "${_ret}" PARENT_SCOPE) 69 | unset(_ret CACHE) 70 | endfunction() 71 | 72 | -------------------------------------------------------------------------------- /pmm/util.cmake: -------------------------------------------------------------------------------- 1 | function(_pmm_read_script_argv var) 2 | set(got_p FALSE) 3 | set(got_script FALSE) 4 | set(ret) 5 | foreach(i RANGE "${CMAKE_ARGC}") 6 | set(arg "${CMAKE_ARGV${i}}") 7 | if(got_p) 8 | if(got_script) 9 | list(APPEND ret "${arg}") 10 | else() 11 | set(got_script TRUE) 12 | endif() 13 | elseif(arg STREQUAL "-P") 14 | set(got_p TRUE) 15 | endif() 16 | endforeach() 17 | set("${var}" "${ret}" PARENT_SCOPE) 18 | endfunction() 19 | 20 | # Argument parser helper. This may look like magic, but it is pretty simple: 21 | # - Call this at the top of a function 22 | # - It takes three "list" arguments: `.`, `-` and `+`. 23 | # - The `.` arguments specify the "option/boolean" values to parse out. 24 | # - The `-` arguments specify the one-value arguments to parse out. 25 | # - The `+` argumenst specify mult-value arguments to parse out. 26 | # - Specify `-nocheck` to disable warning on unparse arguments. 27 | # - Parse values are prefixed with `ARG` 28 | # 29 | # This macro makes use of some very horrible aspects of CMake macros: 30 | # - Values appear the caller's scope, so no need to set(PARENT_SCOPE) 31 | # - The ${${}ARGN} eldritch horror evaluates to the ARGN *OF THE CALLER*, while 32 | # ${ARGN} evaluates to the macro's own ARGN value. This is because ${${}ARGN} 33 | # inhibits macro argument substitution. It is painful, but it makes this magic 34 | # work. 35 | macro(_pmm_parse_args) 36 | cmake_parse_arguments(_ "-nocheck;-hardcheck" "" ".;-;+" "${ARGN}") 37 | set(__arglist "${${}ARGN}") 38 | _pmm_parse_arglist("${__.}" "${__-}" "${__+}") 39 | endmacro() 40 | 41 | macro(_pmm_parse_script_args) 42 | cmake_parse_arguments(_ "-nocheck;-hardcheck" "" ".;-;+" "${ARGV}") 43 | _pmm_read_script_argv(__arglist) 44 | _pmm_parse_arglist("${__.}" "${__-}" "${__+}") 45 | endmacro() 46 | 47 | macro(_pmm_parse_arglist opt args list_args) 48 | cmake_parse_arguments(ARG "${opt}" "${args}" "${list_args}" "${__arglist}") 49 | if(NOT __-nocheck) 50 | foreach(arg IN LISTS ARG_UNPARSED_ARGUMENTS) 51 | message(WARNING "Unknown argument: ${arg}") 52 | endforeach() 53 | if(__-hardcheck AND NOT ("${ARG_UNPARSED_ARGUMENTS}" STREQUAL "")) 54 | message(FATAL_ERROR "Unknown arguments provided.") 55 | endif() 56 | endif() 57 | endmacro() 58 | 59 | macro(_pmm_lift) 60 | foreach(varname IN ITEMS ${ARGN}) 61 | set("${varname}" "${${varname}}" PARENT_SCOPE) 62 | endforeach() 63 | endmacro() 64 | 65 | function(_pmm_exec) 66 | if(PMM_DEBUG) 67 | set(acc) 68 | foreach(arg IN LISTS ARGN) 69 | if(arg MATCHES " |\\\"|\\\\") 70 | string(REPLACE "\"" "\\\"" arg "${arg}") 71 | string(REPLACE "\\" "\\\\" arg "${arg}") 72 | set(arg "\"${arg}\"") 73 | endif() 74 | string(APPEND acc "${arg} ") 75 | endforeach() 76 | _pmm_log(DEBUG "Executing command: ${acc}") 77 | endif() 78 | set(output_args) 79 | if(NOT NO_EAT_OUTPUT IN_LIST ARGN) 80 | set(output_args 81 | OUTPUT_VARIABLE out 82 | ERROR_VARIABLE out 83 | ) 84 | endif() 85 | list(FIND ARGN WORKING_DIRECTORY wd_kw_idx) 86 | set(wd_arg) 87 | if(wd_kw_idx GREATER -1) 88 | math(EXPR wd_idx "${wd_kw_idx} + 1") 89 | list(GET ARGN "${wd_idx}" wd_dir) 90 | LIST(REMOVE_AT ARGN "${wd_idx}" "${wd_kw_idx}") 91 | set(wd_arg WORKING_DIRECTORY "${wd_dir}") 92 | endif() 93 | list(REMOVE_ITEM ARGN NO_EAT_OUTPUT) 94 | execute_process( 95 | COMMAND ${ARGN} 96 | ${output_args} 97 | RESULT_VARIABLE rc 98 | ${wd_arg} 99 | ) 100 | set(_PMM_RC "${rc}" PARENT_SCOPE) 101 | set(_PMM_OUTPUT "${out}" PARENT_SCOPE) 102 | endfunction() 103 | 104 | 105 | function(_pmm_write_if_different filepath content) 106 | set(do_write FALSE) 107 | if(NOT EXISTS "${filepath}") 108 | set(do_write TRUE) 109 | else() 110 | file(READ "${filepath}" cur_content) 111 | if(NOT cur_content STREQUAL content) 112 | set(do_write TRUE) 113 | endif() 114 | endif() 115 | if(do_write) 116 | _pmm_log(DEBUG "Updating contents of file: ${filepath}") 117 | get_filename_component(pardir "${filepath}" DIRECTORY) 118 | file(MAKE_DIRECTORY "${pardir}") 119 | file(WRITE "${filepath}" "${content}") 120 | else() 121 | _pmm_log(DEBUG "Contents of ${filepath} are up-to-date") 122 | endif() 123 | set(_PMM_DID_WRITE "${do_write}" PARENT_SCOPE) 124 | endfunction() 125 | 126 | 127 | function(_pmm_get_var_from_file filepath varname) 128 | include(${filepath}) 129 | get_property(propt VARIABLE PROPERTY ${varname}) 130 | set(${varname} ${propt} PARENT_SCOPE) 131 | endfunction() 132 | 133 | 134 | function(_pmm_generate_cli_scripts force) 135 | # The sh scipt 136 | if(NOT EXISTS "${CMAKE_BINARY_DIR}/pmm-cli.sh" OR ${force}) 137 | file(WRITE "${_PMM_USER_DATA_DIR}/pmm-cli.sh" "#!/bin/sh\n${CMAKE_COMMAND} -P ${PMM_MODULE} \"$@\"") 138 | # Fix to make the sh executable 139 | file(COPY "${_PMM_USER_DATA_DIR}/pmm-cli.sh" 140 | DESTINATION ${CMAKE_BINARY_DIR} 141 | FILE_PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE 142 | ) 143 | endif() 144 | # The bat scipt 145 | if(NOT EXISTS "${CMAKE_BINARY_DIR}/pmm-cli.bat" OR ${force}) 146 | file(WRITE "${CMAKE_BINARY_DIR}/pmm-cli.bat" "@echo off\n\"${CMAKE_COMMAND}\" -P \"${PMM_MODULE}\" %*") 147 | endif() 148 | endfunction() 149 | 150 | 151 | function(_pmm_generate_shim name executable) 152 | # The sh scipt 153 | if (NOT EXISTS "${CMAKE_BINARY_DIR}/${name}.sh") 154 | file(WRITE "${_PMM_USER_DATA_DIR}/${name}.sh" "#!/bin/sh\n\"${executable}\" \"$@\"") 155 | # Fix to make the sh executable 156 | file(COPY "${_PMM_USER_DATA_DIR}/${name}.sh" 157 | DESTINATION ${CMAKE_BINARY_DIR} 158 | FILE_PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE 159 | ) 160 | endif () 161 | # The bat scipt 162 | if (NOT EXISTS "${CMAKE_BINARY_DIR}/${name}.bat") 163 | file(WRITE "${CMAKE_BINARY_DIR}/${name}.bat" "@echo off\n\"${executable}\" %*") 164 | endif () 165 | endfunction() 166 | 167 | 168 | function(_pmm_verbose_lock fpath) 169 | _pmm_parse_args( 170 | . DIRECTORY 171 | - FIRST_MESSAGE FAIL_MESSAGE RESULT_VARIABLE LAST_WAIT_DURATION 172 | ) 173 | set(arg_dir) 174 | if(ARG_DIRECTORY) 175 | set(arg_dir "DIRECTORY") 176 | endif() 177 | if(NOT ARG_LAST_WAIT_DURATION) 178 | set(ARG_LAST_WAIT_DURATION 60) 179 | endif() 180 | set("${ARG_RESULT_VARIABLE}" TRUE PARENT_SCOPE) 181 | file( 182 | LOCK "${fpath}" ${arg_dir} 183 | GUARD PROCESS 184 | TIMEOUT 3 185 | RESULT_VARIABLE lock_res 186 | ) 187 | if(NOT lock_res) 188 | return() 189 | endif() 190 | # Couldn't get the lock 191 | _pmm_log("${ARG_FIRST_MESSAGE}") 192 | file( 193 | LOCK "${fpath}" ${arg_dir} 194 | GUARD PROCESS 195 | TIMEOUT 60 196 | RESULT_VARIABLE lock_res 197 | ) 198 | if(NOT lock_res) 199 | return() 200 | endif() 201 | _pmm_log("Unable to obtain lock after 60 seconds. We'll try for ${ARG_LAST_WAIT_DURATION} more seconds...") 202 | file( 203 | LOCK "${fpath}" ${arg_dir} 204 | GUARD PROCESS 205 | TIMEOUT "${ARG_LAST_WAIT_DURATION}" 206 | RESULT_VARIABLE lock_res 207 | ) 208 | if(lock_res) 209 | message(WARNING "${ARG_FAIL_MESSAGE}") 210 | set("${ARG_RESULT_VARIABLE}" FALSE PARENT_SCOPE) 211 | endif() 212 | endfunction() -------------------------------------------------------------------------------- /pmm/vcpkg.cmake: -------------------------------------------------------------------------------- 1 | # Download vcpkg at revision `rev` and place the built result in `dir` 2 | function(_pmm_ensure_vcpkg dir rev) 3 | _pmm_verbose_lock( 4 | "${dir}.lock" 5 | FIRST_MESSAGE "Another CMake instance is bootstrapping vcpkg. Please wait..." 6 | FAIL_MESSAGE "Unable to obtain vcpkg bootstrapping lock. Check if there is a stuck process holding it open." 7 | RESULT_VARIABLE did_lock 8 | LAST_WAIT_DURATION 240 9 | ) 10 | if(NOT did_lock) 11 | message(FATAL_ERROR "Unable to obtain exclusive lock on directory ${_PMM_CONAN_MANAGED_VENV_DIR}. Abort.") 12 | endif() 13 | # The final executable is deterministically placed: 14 | set(PMM_VCPKG_EXECUTABLE "${dir}/vcpkg${CMAKE_EXECUTABLE_SUFFIX}" 15 | CACHE FILEPATH 16 | "Path to vcpkg for this project" 17 | FORCE 18 | ) 19 | _pmm_log(DEBUG "Expecting vcpkg executable at ${PMM_VCPKG_EXECUTABLE}") 20 | # Check if the vcpkg exe already exists, which means we've already 21 | # bootstrapped and installed it 22 | if(EXISTS "${PMM_VCPKG_EXECUTABLE}") 23 | file(LOCK "${dir}.lock" RELEASE) 24 | return() 25 | endif() 26 | # We do the build in a temporary directory, then rename that temporary dir 27 | # to the final dir 28 | set(tmp_dir "${_PMM_USER_DATA_DIR}/vcpkg-tmp") 29 | # Ignore any existing temporary files. They shouldn't be there unless there 30 | # was an error 31 | file(REMOVE_RECURSE "${tmp_dir}") 32 | # Download the Zip archive from GitHub 33 | get_filename_component(vcpkg_zip "${dir}/../vcpkg-tmp.zip" ABSOLUTE) 34 | set(url "https://github.com/Microsoft/vcpkg/archive/${rev}.zip") 35 | _pmm_log("Downloading vcpkg at ${rev} ...") 36 | _pmm_log(VERBOSE "vcpkg ZIP archive lives at ${url}") 37 | file( 38 | DOWNLOAD "${url}" "${vcpkg_zip}" 39 | STATUS st 40 | SHOW_PROGRESS 41 | TLS_VERIFY ON 42 | ) 43 | list(GET st 0 rc) 44 | list(GET st 1 msg) 45 | if(rc) 46 | message(FATAL_ERROR "Failed to download vcpkg [${rc}]: ${msg}") 47 | endif() 48 | # Extract the vcpkg archive into the temporary directory 49 | _pmm_log("Extracting vcpkg archive...") 50 | file(MAKE_DIRECTORY "${tmp_dir}") 51 | execute_process( 52 | COMMAND ${CMAKE_COMMAND} -E tar xf "${vcpkg_zip}" 53 | WORKING_DIRECTORY "${tmp_dir}" 54 | ) 55 | # There should be one root directory that was extracted. 56 | file(GLOB vcpkg_root "${tmp_dir}/*") 57 | list(LENGTH vcpkg_root len) 58 | if(NOT len EQUAL 1) 59 | message(FATAL_ERROR "More than one directory extracted from downloaded vcpkg [??]") 60 | endif() 61 | # Remove the zip file since we don't need it any more 62 | file(REMOVE "${vcpkg_zip}") 63 | if(CMAKE_HOST_WIN32) 64 | set(bootstrap_ext bat) 65 | else() 66 | set(bootstrap_ext sh) 67 | endif() 68 | # Run the bootstrap script to prepare the tool 69 | _pmm_log("Bootstrapping the vcpkg tool (This may take a minute)...") 70 | set(no_eat) 71 | if(PMM_DEBUG) 72 | set(no_eat NO_EAT_OUTPUT) 73 | endif() 74 | _pmm_exec( 75 | ${CMAKE_COMMAND} -E env 76 | CC=${CMAKE_C_COMPILER} 77 | CXX=${CMAKE_CXX_COMPILER} 78 | "${vcpkg_root}/bootstrap-vcpkg.${bootstrap_ext}" 79 | ${no_eat} 80 | ) 81 | if(_PMM_RC) 82 | message(FATAL_ERROR "Failed to Bootstrap the vcpkg tool [${_PMM_RC}]:\n${_PMM_OUTPUT}") 83 | endif() 84 | _pmm_log("Testing bootstrapped vcpkg") 85 | _pmm_exec("${vcpkg_root}/vcpkg${CMAKE_EXECUTABLE_SUFFIX}" version) 86 | if(_PMM_RC) 87 | message(FATAL_ERROR "Failed to execute generated vcpkg tool [${_PMM_RC}]:\n:${_PMM_OUTPUT}") 88 | endif() 89 | # Move the temporary directory to the final directory path 90 | file(REMOVE_RECURSE "${dir}") 91 | file(RENAME "${vcpkg_root}" "${dir}") 92 | _pmm_log("vcpkg successfully bootstrapped to ${dir}") 93 | # Fix for "Could not detect vcpkg-root." 94 | execute_process(COMMAND ${CMAKE_COMMAND} -E sleep 1) 95 | # Release the exclusive lock on the directory we obtained at the top of this fn 96 | file(LOCK "${dir}.lock" RELEASE) 97 | endfunction() 98 | 99 | function(_pmm_vcpkg_default_triplet out) 100 | string(TOLOWER "${CMAKE_GENERATOR_PLATFORM}" plat) 101 | string(TOLOWER "${CMAKE_GENERATOR}" gen) 102 | set(compiler "${CMAKE_CXX_COMPILER}") 103 | if(NOT compiler) 104 | set(compiler "${CMAKE_C_COMPILER}") 105 | endif() 106 | # Determine that thing! 107 | if(FALSE) 108 | # Just for alignment 109 | elseif(plat STREQUAL "win32") 110 | set(arch x86) 111 | elseif(plat STREQUAL "x64") 112 | set(arch x64) 113 | elseif(plat STREQUAL "arm") 114 | set(arch arm) 115 | elseif(plat STREQUAL "arm64") 116 | set(arch arm64) 117 | else() 118 | if(gen MATCHES "^Visual Studio 1. 20.. Win64$") 119 | set(arch x64) 120 | elseif(gen MATCHES "^Visual Studio 1. 20.. ARM$") 121 | set(arch arm) 122 | elseif(gen MATCHES "^Visual Studio 1. 20..$") 123 | set(arch x86) 124 | else() 125 | if(compiler MATCHES "(amd64|x64)/cl.exe$") 126 | set(arch x64) 127 | elseif(compiler MATCHES "arm/cl.exe$") 128 | set(arch arm) 129 | elseif(compiler MATCHES "arm64/cl.exe$") 130 | set(arch arm64) 131 | elseif(compiler MATCHES "(bin|x64)/cl.exe$") 132 | set(arch x86) 133 | elseif(CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL "x86_64") 134 | set(arch x64) 135 | elseif(CMAKE_SIZEOF_VOID_P EQUAL 4) 136 | set(arch x86) 137 | elseif(CMAKE_SIZEOF_VOID_P EQUAL 8) 138 | set(arch x64) 139 | else() 140 | message(FATAL_ERROR "Unable to determine target triple for vcpkg.") 141 | endif() 142 | endif() 143 | endif() 144 | 145 | set(sysname "${CMAKE_SYSTEM_NAME}") 146 | if(NOT sysname AND CMAKE_HOST_SYSTEM_NAME) 147 | set(sysname "${CMAKE_HOST_SYSTEM_NAME}") 148 | endif() 149 | if(sysname MATCHES "^Windows(Store|Phone)$") 150 | set(platform uwp) 151 | elseif(sysname STREQUAL "Linux") 152 | set(platform linux) 153 | elseif(sysname STREQUAL "Darwin") 154 | set(platform osx) 155 | elseif(sysname STREQUAL "Windows") 156 | set(platform windows) 157 | elseif(sysname STREQUAL "FreeBSD") 158 | set(platform freebsd) 159 | endif() 160 | set(${out} "${arch}-${platform}" PARENT_SCOPE) 161 | endfunction() 162 | 163 | function(_pmm_vcpkg_copy_custom_ports ports_list) 164 | foreach(port IN LISTS ports_list) 165 | # Port name is based on the directory name 166 | get_filename_component(port_name ${port} NAME) 167 | if(NOT EXISTS "${port}/portfile.cmake") 168 | message(FATAL_ERROR "Failed find portfile in ${port}!") 169 | endif() 170 | 171 | # Prepare a directory for this port 172 | set(port_dest_dir "${__vcpkg_inst_dir}/ports/${port_name}") 173 | if(EXISTS "${port_dest_dir}" AND NOT EXISTS "${port_dest_dir}/CUSTOM_PORT_FROM_PMM.txt") 174 | message(WARNING "Portfile already included by default!") 175 | elseif(NOT EXISTS "${port_dest_dir}/CUSTOM_PORT_FROM_PMM.txt") 176 | file(MAKE_DIRECTORY "${port_dest_dir}") 177 | # Use this stamp file to tell others/ourself this is a Port copied by PMM 178 | file(WRITE "${port_dest_dir}/CUSTOM_PORT_FROM_PMM.txt" "This is a custom port copied by PMM") 179 | endif() 180 | 181 | # Copy all files from the port: 182 | file(GLOB port_files "${port}/*") 183 | foreach(port_file_src IN LISTS port_files) 184 | get_filename_component(port_file_name ${port_file_src} NAME) 185 | set(port_file_dest "${port_dest_dir}/${port_file_name}") 186 | file(TIMESTAMP ${port_file_dest} port_file_location_ts) 187 | file(TIMESTAMP ${port_file_src} port_file_ts) 188 | _pmm_log(DEBUG "Timestamp: ${port_file_location_ts} for ${port_file_dest}") 189 | _pmm_log(DEBUG "Timestamp: ${port_file_ts} for ${port_file_src}") 190 | if(NOT "${port_file_ts}" STREQUAL "${port_file_location_ts}") 191 | _pmm_log(VERBOSE "${port_name}: Copying ${port_file_src} to ${port_file_dest}") 192 | file(COPY "${port_file_src}" DESTINATION "${__vcpkg_inst_dir}/ports/${port_name}/") 193 | else() 194 | _pmm_log(VERBOSE "${port_name}: ${port_file_name} is up to date") 195 | endif() 196 | endforeach() 197 | endforeach() 198 | endfunction() 199 | 200 | function(_pmm_vcpkg) 201 | _pmm_parse_args( 202 | - REVISION TRIPLET 203 | + REQUIRES PORTS OVERLAY_PORTS OVERLAY_TRIPLETS 204 | ) 205 | 206 | if(NOT DEFINED ARG_REVISION) 207 | message(FATAL_ERROR "Using pmm(VCPKG) requires a REVISION argument. Try `REVISION 2022.05.10`") 208 | endif() 209 | if(NOT DEFINED ARG_TRIPLET) 210 | _pmm_vcpkg_default_triplet(ARG_TRIPLET) 211 | endif() 212 | 213 | _pmm_log(VERBOSE "Using vcpkg target triplet ${ARG_TRIPLET}") 214 | get_filename_component(__vcpkg_inst_dir "${_PMM_USER_DATA_DIR}/vcpkg-${ARG_REVISION}" ABSOLUTE) 215 | _pmm_log(DEBUG "vcpkg directory is ${__vcpkg_inst_dir}") 216 | set(prev "${PMM_VCPKG_EXECUTABLE}") 217 | 218 | _pmm_ensure_vcpkg("${__vcpkg_inst_dir}" "${ARG_REVISION}") 219 | if(NOT prev STREQUAL PMM_VCPKG_EXECUTABLE) 220 | _pmm_log("Using vcpkg executable: ${PMM_VCPKG_EXECUTABLE}") 221 | endif() 222 | 223 | if(DEFINED ARG_PORTS) 224 | _pmm_vcpkg_copy_custom_ports("${ARG_PORTS}") 225 | endif() 226 | 227 | set(vcpkg_install_args 228 | --triplet "${ARG_TRIPLET}" 229 | --recurse 230 | ${ARG_REQUIRES} 231 | ) 232 | 233 | foreach(overlay IN LISTS ARG_OVERLAY_PORTS) 234 | get_filename_component(overlay "${overlay}" ABSOLUTE) 235 | list(APPEND vcpkg_install_args "--overlay-ports=${overlay}") 236 | endforeach() 237 | 238 | foreach(triplet IN LISTS ARG_OVERLAY_TRIPLETS) 239 | get_filename_component(triplet "${triplet}" ABSOLUTE) 240 | list(APPEND vcpkg_install_args "--overlay-triplets=${triplet}") 241 | endforeach() 242 | 243 | if(ARG_REQUIRES) 244 | _pmm_log("Installing requirements with vcpkg") 245 | set(cmd ${CMAKE_COMMAND} -E env 246 | VCPKG_ROOT=${__vcpkg_inst_dir} 247 | CC=${CMAKE_C_COMPILER} 248 | CXX=${CMAKE_CXX_COMPILER} 249 | "${PMM_VCPKG_EXECUTABLE}" install ${vcpkg_install_args} 250 | ) 251 | set(install_lock "${PMM_VCPKG_EXECUTABLE}.install-lock") 252 | _pmm_verbose_lock( 253 | "${install_lock}" 254 | FIRST_MESSAGE "Another 'vcpkg install' process is running. Wait..." 255 | FAIL_MESSAGE "Unable to obtain an exclusive lock on the install process. Will continue anyway, but may fail spuriously" 256 | ) 257 | _pmm_exec(${cmd} NO_EAT_OUTPUT) 258 | file(LOCK "${install_lock}" RELEASE) 259 | if(_PMM_RC) 260 | message(FATAL_ERROR "Failed to install requirements with vcpkg [${_PMM_RC}]:\n${_PMM_OUTPUT}") 261 | else() 262 | _pmm_log(DEBUG "vcpkg output:\n${_PMM_OUTPUT}") 263 | endif() 264 | endif() 265 | set(_PMM_INCLUDE "${__vcpkg_inst_dir}/scripts/buildsystems/vcpkg.cmake" PARENT_SCOPE) 266 | _pmm_generate_shim(vcpkg "${PMM_VCPKG_EXECUTABLE}") 267 | endfunction() 268 | -------------------------------------------------------------------------------- /poetry.lock: -------------------------------------------------------------------------------- 1 | [[package]] 2 | name = "atomicwrites" 3 | version = "1.4.0" 4 | description = "Atomic file writes." 5 | category = "main" 6 | optional = false 7 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 8 | 9 | [[package]] 10 | name = "attrs" 11 | version = "21.4.0" 12 | description = "Classes Without Boilerplate" 13 | category = "main" 14 | optional = false 15 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" 16 | 17 | [package.extras] 18 | dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "furo", "sphinx", "sphinx-notfound-page", "pre-commit", "cloudpickle"] 19 | docs = ["furo", "sphinx", "zope.interface", "sphinx-notfound-page"] 20 | tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "cloudpickle"] 21 | tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "cloudpickle"] 22 | 23 | [[package]] 24 | name = "colorama" 25 | version = "0.4.5" 26 | description = "Cross-platform colored terminal text." 27 | category = "main" 28 | optional = false 29 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" 30 | 31 | [[package]] 32 | name = "iniconfig" 33 | version = "1.1.1" 34 | description = "iniconfig: brain-dead simple config-ini parsing" 35 | category = "main" 36 | optional = false 37 | python-versions = "*" 38 | 39 | [[package]] 40 | name = "nodeenv" 41 | version = "1.7.0" 42 | description = "Node.js virtual environment builder" 43 | category = "main" 44 | optional = false 45 | python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*" 46 | 47 | [[package]] 48 | name = "packaging" 49 | version = "21.3" 50 | description = "Core utilities for Python packages" 51 | category = "main" 52 | optional = false 53 | python-versions = ">=3.6" 54 | 55 | [package.dependencies] 56 | pyparsing = ">=2.0.2,<3.0.5 || >3.0.5" 57 | 58 | [[package]] 59 | name = "pluggy" 60 | version = "1.0.0" 61 | description = "plugin and hook calling mechanisms for python" 62 | category = "main" 63 | optional = false 64 | python-versions = ">=3.6" 65 | 66 | [package.extras] 67 | dev = ["pre-commit", "tox"] 68 | testing = ["pytest", "pytest-benchmark"] 69 | 70 | [[package]] 71 | name = "py" 72 | version = "1.11.0" 73 | description = "library with cross-python path, ini-parsing, io, code, log facilities" 74 | category = "main" 75 | optional = false 76 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" 77 | 78 | [[package]] 79 | name = "pyparsing" 80 | version = "3.0.9" 81 | description = "pyparsing module - Classes and methods to define and execute parsing grammars" 82 | category = "main" 83 | optional = false 84 | python-versions = ">=3.6.8" 85 | 86 | [package.extras] 87 | diagrams = ["railroad-diagrams", "jinja2"] 88 | 89 | [[package]] 90 | name = "pyright" 91 | version = "1.1.255" 92 | description = "Command line wrapper for pyright" 93 | category = "main" 94 | optional = false 95 | python-versions = ">=3.7" 96 | 97 | [package.dependencies] 98 | nodeenv = ">=1.6.0" 99 | 100 | [package.extras] 101 | all = ["twine (>=3.4.1)"] 102 | dev = ["twine (>=3.4.1)"] 103 | 104 | [[package]] 105 | name = "pytest" 106 | version = "7.1.2" 107 | description = "pytest: simple powerful testing with Python" 108 | category = "main" 109 | optional = false 110 | python-versions = ">=3.7" 111 | 112 | [package.dependencies] 113 | atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""} 114 | attrs = ">=19.2.0" 115 | colorama = {version = "*", markers = "sys_platform == \"win32\""} 116 | iniconfig = "*" 117 | packaging = "*" 118 | pluggy = ">=0.12,<2.0" 119 | py = ">=1.8.2" 120 | tomli = ">=1.0.0" 121 | 122 | [package.extras] 123 | testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "xmlschema"] 124 | 125 | [[package]] 126 | name = "toml" 127 | version = "0.10.2" 128 | description = "Python Library for Tom's Obvious, Minimal Language" 129 | category = "main" 130 | optional = false 131 | python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" 132 | 133 | [[package]] 134 | name = "tomli" 135 | version = "2.0.1" 136 | description = "A lil' TOML parser" 137 | category = "main" 138 | optional = false 139 | python-versions = ">=3.7" 140 | 141 | [[package]] 142 | name = "yapf" 143 | version = "0.32.0" 144 | description = "A formatter for Python code." 145 | category = "main" 146 | optional = false 147 | python-versions = "*" 148 | 149 | [metadata] 150 | lock-version = "1.1" 151 | python-versions = "^3.10" 152 | content-hash = "58d1e275585a7980d71d20b56fff934f6dcaa88a76f89cfc7c73476d0a6b4f9b" 153 | 154 | [metadata.files] 155 | atomicwrites = [ 156 | {file = "atomicwrites-1.4.0-py2.py3-none-any.whl", hash = "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197"}, 157 | {file = "atomicwrites-1.4.0.tar.gz", hash = "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a"}, 158 | ] 159 | attrs = [ 160 | {file = "attrs-21.4.0-py2.py3-none-any.whl", hash = "sha256:2d27e3784d7a565d36ab851fe94887c5eccd6a463168875832a1be79c82828b4"}, 161 | {file = "attrs-21.4.0.tar.gz", hash = "sha256:626ba8234211db98e869df76230a137c4c40a12d72445c45d5f5b716f076e2fd"}, 162 | ] 163 | colorama = [ 164 | {file = "colorama-0.4.5-py2.py3-none-any.whl", hash = "sha256:854bf444933e37f5824ae7bfc1e98d5bce2ebe4160d46b5edf346a89358e99da"}, 165 | {file = "colorama-0.4.5.tar.gz", hash = "sha256:e6c6b4334fc50988a639d9b98aa429a0b57da6e17b9a44f0451f930b6967b7a4"}, 166 | ] 167 | iniconfig = [ 168 | {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"}, 169 | {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"}, 170 | ] 171 | nodeenv = [ 172 | {file = "nodeenv-1.7.0-py2.py3-none-any.whl", hash = "sha256:27083a7b96a25f2f5e1d8cb4b6317ee8aeda3bdd121394e5ac54e498028a042e"}, 173 | {file = "nodeenv-1.7.0.tar.gz", hash = "sha256:e0e7f7dfb85fc5394c6fe1e8fa98131a2473e04311a45afb6508f7cf1836fa2b"}, 174 | ] 175 | packaging = [ 176 | {file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"}, 177 | {file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"}, 178 | ] 179 | pluggy = [ 180 | {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, 181 | {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, 182 | ] 183 | py = [ 184 | {file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"}, 185 | {file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"}, 186 | ] 187 | pyparsing = [ 188 | {file = "pyparsing-3.0.9-py3-none-any.whl", hash = "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc"}, 189 | {file = "pyparsing-3.0.9.tar.gz", hash = "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb"}, 190 | ] 191 | pyright = [ 192 | {file = "pyright-1.1.255-py3-none-any.whl", hash = "sha256:3ad2789f51b261c48bf312b71275232e97ea30ad880fa920e1d667420d30a1a9"}, 193 | {file = "pyright-1.1.255.tar.gz", hash = "sha256:ccfe8796a0264fcaa2148bc4b9c348e99ad4e77482307371bd0682bd20c6617c"}, 194 | ] 195 | pytest = [ 196 | {file = "pytest-7.1.2-py3-none-any.whl", hash = "sha256:13d0e3ccfc2b6e26be000cb6568c832ba67ba32e719443bfe725814d3c42433c"}, 197 | {file = "pytest-7.1.2.tar.gz", hash = "sha256:a06a0425453864a270bc45e71f783330a7428defb4230fb5e6a731fde06ecd45"}, 198 | ] 199 | toml = [ 200 | {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, 201 | {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, 202 | ] 203 | tomli = [ 204 | {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, 205 | {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, 206 | ] 207 | yapf = [ 208 | {file = "yapf-0.32.0-py2.py3-none-any.whl", hash = "sha256:8fea849025584e486fd06d6ba2bed717f396080fd3cc236ba10cb97c4c51cf32"}, 209 | {file = "yapf-0.32.0.tar.gz", hash = "sha256:a3f5085d37ef7e3e004c4ba9f9b3e40c54ff1901cd111f05145ae313a7c67d1b"}, 210 | ] 211 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "pmm" 3 | version = "0.1.0" 4 | description = "The Package Manager Manager" 5 | authors = ["vector-of-bool "] 6 | packages = [{include = "ci/"}] 7 | 8 | [tool.poetry.dependencies] 9 | python = "^3.10" 10 | pytest = "^7.1.2" 11 | pyright = "^1.1.255" 12 | yapf = "^0.32.0" 13 | toml = "^0.10.2" 14 | 15 | [tool.poetry.dev-dependencies] 16 | 17 | [build-system] 18 | requires = ["poetry-core>=1.0.0"] 19 | build-backend = "poetry.core.masonry.api" 20 | 21 | [tool.yapf] 22 | based_on_style = "pep8" 23 | column_limit = 120 -------------------------------------------------------------------------------- /tests/BuildContainer.cmake: -------------------------------------------------------------------------------- 1 | include(${CMAKE_CURRENT_LIST_DIR}/../pmm/util.cmake) 2 | _pmm_parse_script_args( 3 | . /Verbose 4 | - /Docker /ContainerName /DockerBuildDir 5 | ) 6 | 7 | find_program(ARG_/Docker docker) 8 | 9 | if(NOT ARG_/Verbose) 10 | set(OUTPUT_EAT_ARGS ERROR_VARIABLE out OUTPUT_VARIABLE out) 11 | endif() 12 | 13 | function(run_checked) 14 | execute_process( 15 | ${OUTPUT_EAT_ARGS} 16 | RESULT_VARIABLE retc 17 | COMMAND ${ARGN} 18 | ) 19 | if(retc) 20 | string(REPLACE ";" " " cmd "${ARGN}") 21 | message(FATAL_ERROR "Running command (${cmd}) failed [${retc}]: ${out}") 22 | endif() 23 | endfunction() 24 | 25 | message(STATUS "Building Docker container") 26 | run_checked(${ARG_/Docker} build -t "${ARG_/ContainerName}" "${ARG_/DockerBuildDir}") 27 | -------------------------------------------------------------------------------- /tests/BuildTestProject.cmake: -------------------------------------------------------------------------------- 1 | include(${CMAKE_CURRENT_LIST_DIR}/../pmm/util.cmake) 2 | _pmm_parse_script_args( 3 | - /SourceDir /BinaryDir /Generator 4 | ) 5 | 6 | get_filename_component(root "${CMAKE_CURRENT_LIST_DIR}/.." ABSOLUTE) 7 | set(pmm_dir "${root}/pmm") 8 | 9 | # Configure 10 | execute_process( 11 | COMMAND "${CMAKE_COMMAND}" 12 | "-H${ARG_/SourceDir}" 13 | "-B${ARG_/BinaryDir}" 14 | "-DPMM_URL=file://${pmm_dir}" 15 | "-DPMM_INCLUDE=${root}/pmm.cmake" 16 | RESULT_VARIABLE retc 17 | ) 18 | if(retc) 19 | message(FATAL_ERROR "Project configuration failed [${retc}]") 20 | endif() 21 | 22 | # Build 23 | execute_process( 24 | COMMAND "${CMAKE_COMMAND}" --build "${ARG_/BinaryDir}" 25 | RESULT_VARIABLE retc 26 | ) 27 | if(retc) 28 | message(FATAL_ERROR "Project build failed [${retc}]") 29 | endif() 30 | 31 | # Test 32 | execute_process( 33 | COMMAND "${CMAKE_CTEST_COMMAND}" 34 | WORKING_DIRECTORY "${ARG_/BinaryDir}" 35 | RESULT_VARIABLE retc 36 | ) 37 | if(retc) 38 | message(FATAL_ERROR "Project test failed [${retc}]") 39 | endif() 40 | -------------------------------------------------------------------------------- /tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | find_program(DOCKER_EXECUTABLE docker DOC "Path to Docker") 2 | 3 | file(GLOB docker_dirs 4 | CONFIGURE_DEPENDS 5 | *.docker/ 6 | LIST_DIRECTORIES true 7 | ) 8 | 9 | file(GLOB test_project_dirs 10 | CONFIGURE_DEPENDS 11 | *.test-project/ 12 | LIST_DIRECTORIES true 13 | ) 14 | 15 | get_filename_component(DOCKER_TEST_SCRIPT RunDockerTest.cmake ABSOLUTE) 16 | get_filename_component(BUILD_CONTAINER_SCRIPT BuildContainer.cmake ABSOLUTE) 17 | 18 | option(RUN_DOCKER_TESTS "Should we run the Docker tests?" TRUE) 19 | if(DOCKER_EXECUTABLE AND RUN_DOCKER_TESTS) 20 | foreach(dir IN LISTS docker_dirs) 21 | get_filename_component(env "${dir}" NAME_WE) 22 | set(container "pmm.test_container.${env}") 23 | set(fixture_name pmm.docker.${env}) 24 | add_test( 25 | NAME ${fixture_name}/setup 26 | COMMAND ${CMAKE_COMMAND} 27 | -P ${BUILD_CONTAINER_SCRIPT} 28 | /ContainerName ${container} 29 | /Docker ${DOCKER_EXECUTABLE} 30 | /DockerBuildDir "${dir}" 31 | ) 32 | set_property(TEST "${fixture_name}/setup" PROPERTY FIXTURES_SETUP "${fixture_name}") 33 | foreach(project IN LISTS test_project_dirs) 34 | get_filename_component(case "${project}" NAME_WE) 35 | set(test pmm.docker.${env}.${case}) 36 | add_test( 37 | NAME "${test}" 38 | COMMAND ${CMAKE_COMMAND} 39 | -P ${DOCKER_TEST_SCRIPT} 40 | /ContainerName ${container} 41 | /Docker ${DOCKER_EXECUTABLE} 42 | /ProjectDir "${project}" 43 | ) 44 | set_tests_properties("${test}" PROPERTIES 45 | FIXTURES_REQUIRED "${fixture_name}" 46 | SKIP_REGULAR_EXPRESSION "@ctest-skip@" 47 | ) 48 | endforeach() 49 | endforeach() 50 | endif() 51 | 52 | if(NOT NO_CI_TEST_DIR) 53 | add_subdirectory(ci) 54 | endif() 55 | 56 | add_test( 57 | NAME pmm.cli.conan.generate-profile 58 | COMMAND 59 | ${CMAKE_COMMAND} 60 | -DPMM_URL=file://${PROJECT_SOURCE_DIR}/pmm 61 | -DPMM_ALWAYS_DOWNLOAD=1 62 | -P "${PROJECT_SOURCE_DIR}/pmm.cmake" 63 | /Conan 64 | /GenProfile 65 | /Profile ${PROJECT_BINARY_DIR}/dummy-test.profile 66 | ) 67 | -------------------------------------------------------------------------------- /tests/RunDockerTest.cmake: -------------------------------------------------------------------------------- 1 | include(${CMAKE_CURRENT_LIST_DIR}/../pmm/util.cmake) 2 | _pmm_parse_script_args( 3 | . /Verbose 4 | - /Docker /ContainerName /ProjectDir 5 | ) 6 | 7 | find_program(ARG_/Docker docker) 8 | 9 | set(test_pr_dir "${ARG_/ProjectDir}") 10 | 11 | function(run_checked) 12 | execute_process( 13 | RESULT_VARIABLE retc 14 | COMMAND ${ARGN} 15 | ) 16 | if(retc) 17 | string(REPLACE ";" " " cmd "${ARGN}") 18 | message(FATAL_ERROR "Running command (${cmd}) failed [${retc}]: ${out}") 19 | endif() 20 | endfunction() 21 | 22 | get_filename_component(pmm_dir "${CMAKE_CURRENT_LIST_DIR}/../pmm" ABSOLUTE) 23 | set(mount_args 24 | -v "${test_pr_dir}:/host/source:ro" 25 | -v "${CMAKE_CURRENT_LIST_DIR}/../pmm:/host/pmm:ro" 26 | -v "${CMAKE_CURRENT_LIST_DIR}/../pmm.cmake:/host/pmm.cmake:ro" 27 | -v "${CMAKE_CURRENT_LIST_DIR}/..:/host/pmm-repo:ro" 28 | ) 29 | run_checked(${ARG_/Docker} run --rm -t ${mount_args} ${ARG_/ContainerName} 30 | cmake -P /host/pmm-repo/tests/BuildTestProject.cmake 31 | /SourceDir /host/source 32 | /BinaryDir /tmp/build 33 | /Generator "Unix Makefiles" 34 | ) 35 | -------------------------------------------------------------------------------- /tests/alpine.docker/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine:3.14 2 | 3 | RUN apk add wget curl python3 gcc g++ make perl unzip cmake ninja 4 | 5 | env PMM_VCPKG_TEST_SKIP=1 -------------------------------------------------------------------------------- /tests/c7.docker/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM centos/devtoolset-7-toolchain-centos7:7 2 | 3 | USER 0 4 | RUN yum -y install wget curl python3 python3-venv unzip 5 | 6 | RUN wget https://github.com/Kitware/CMake/releases/download/v3.13.0/cmake-3.13.0-Linux-x86_64.sh -nv -O /tmp/cmake.sh 7 | RUN sh /tmp/cmake.sh --exclude-subdir --prefix=/usr/local 8 | 9 | # Note: vcpkg requiers a newer git than the Git with rhel7. This is possible by 10 | # downloading and building a new one from source, but that's a big ask. 11 | ENV PMM_VCPKG_TEST_SKIP=1 12 | 13 | USER 1001 -------------------------------------------------------------------------------- /tests/c8.docker/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM rockylinux:8 2 | 3 | RUN yum -y install wget curl python3 gcc gcc-c++ make unzip git zip tar 4 | 5 | RUN wget https://github.com/Kitware/CMake/releases/download/v3.13.0/cmake-3.13.0-Linux-x86_64.sh -nv -O /tmp/cmake.sh 6 | RUN sh /tmp/cmake.sh --exclude-subdir --prefix=/usr/local 7 | -------------------------------------------------------------------------------- /tests/ci/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(common_test_args) 2 | 3 | function(add_pmm_ci_test name) 4 | _pmm_parse_args( 5 | -hardcheck 6 | - GENERATOR SOURCE_DIR 7 | ) 8 | get_filename_component(dirpath "${ARG_SOURCE_DIR}" ABSOLUTE) 9 | pmm_option(ARG_GENERATOR "${CMAKE_GENERATOR}") 10 | add_test( 11 | NAME "pmm.test.ci.${name}" 12 | COMMAND "${CMAKE_COMMAND}" 13 | -D "PMM_URL=file://${PROJECT_SOURCE_DIR}/pmm" 14 | -D "PMM_INCLUDE=${PROJECT_SOURCE_DIR}/pmm.cmake" 15 | -D "GENERATOR=${ARG_GENERATOR}" 16 | -D "BUILD_DIR=${CMAKE_CURRENT_BINARY_DIR}/${name}" 17 | -D "SOURCE_DIR=${dirpath}" 18 | -P "${CMAKE_CURRENT_SOURCE_DIR}/RunCITest.cmake" 19 | ) 20 | endfunction() 21 | 22 | foreach(subdir conan vcpkg cmcm vcpkg-overlay) 23 | add_pmm_ci_test("${subdir}" SOURCE_DIR "${subdir}") 24 | endforeach() 25 | -------------------------------------------------------------------------------- /tests/ci/RunCITest.cmake: -------------------------------------------------------------------------------- 1 | file(REMOVE_RECURSE "${BUILD_DIR}") 2 | 3 | function(_build config) 4 | message(STATUS "Building config '${config}'") 5 | execute_process( 6 | COMMAND ${CMAKE_COMMAND} 7 | -D CMAKE_BUILD_TYPE=${config} 8 | -D PMM_URL=${PMM_URL} 9 | -D PMM_INCLUDE=${PMM_INCLUDE} 10 | -D PMM_DEBUG=TRUE 11 | -D PMM_ALWAYS_DOWNLOAD=TRUE 12 | -G ${GENERATOR} 13 | "-H${SOURCE_DIR}" 14 | "-B${BUILD_DIR}" 15 | RESULT_VARIABLE retc 16 | ) 17 | 18 | if(retc) 19 | message(FATAL_ERROR "Configure failed [${retc}]") 20 | endif() 21 | 22 | execute_process( 23 | COMMAND ${CMAKE_COMMAND} --build ${BUILD_DIR} 24 | RESULT_VARIABLE retc 25 | ) 26 | 27 | if(retc) 28 | message(FATAL_ERROR "Build failed [${retc}]") 29 | endif() 30 | endfunction() 31 | 32 | _build(Debug) 33 | _build(Release) 34 | -------------------------------------------------------------------------------- /tests/ci/cmcm/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | project(TestProject) 3 | 4 | include(${PMM_INCLUDE}) 5 | pmm(CMakeCM ROLLING) 6 | 7 | include(CMakeRC) 8 | 9 | cmrc_add_resource_library(dummy_rc CMakeLists.txt) 10 | add_executable(dummy-program main.cpp) 11 | target_link_libraries(dummy-program PRIVATE dummy_rc) 12 | -------------------------------------------------------------------------------- /tests/ci/cmcm/main.cpp: -------------------------------------------------------------------------------- 1 | int main() {} -------------------------------------------------------------------------------- /tests/ci/conan/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | project(TestProject) 3 | 4 | message(STATUS "PMM URL is ${PMM_URL}") 5 | include(${PMM_INCLUDE}) 6 | pmm(CONAN DEBUG) 7 | 8 | add_executable(dummy main.cpp) 9 | target_link_libraries(dummy PRIVATE CONAN_PKG::spdlog) 10 | -------------------------------------------------------------------------------- /tests/ci/conan/conanfile.py: -------------------------------------------------------------------------------- 1 | import conans 2 | 3 | 4 | class CITestConanProject(conans.ConanFile): 5 | name = 'CITestProject' 6 | version = '1.2.3' 7 | generators = 'cmake' 8 | requires = ('spdlog/1.4.2') 9 | -------------------------------------------------------------------------------- /tests/ci/conan/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main() { 5 | auto console = spdlog::stdout_color_mt("console"); 6 | console->info("Hello!"); 7 | } -------------------------------------------------------------------------------- /tests/ci/vcpkg-overlay/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | project(TestProject) 3 | 4 | message(STATUS "PMM URL is ${PMM_URL}") 5 | include(${PMM_INCLUDE}) 6 | pmm(VCPKG 7 | REVISION 2022.05.10 8 | REQUIRES nlohmann-json[test-feature] 9 | OVERLAY_PORTS custom-ports/ custom-ports2/ 10 | DEBUG 11 | ) 12 | 13 | find_package(nlohmann_json REQUIRED CONFIG) 14 | 15 | add_executable(dummy main.cpp) 16 | target_link_libraries(dummy PRIVATE nlohmann_json::nlohmann_json) 17 | -------------------------------------------------------------------------------- /tests/ci/vcpkg-overlay/custom-ports/.empty: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vector-of-bool/pmm/3750c8e0b84c93669f9c7054b091788715c4dc10/tests/ci/vcpkg-overlay/custom-ports/.empty -------------------------------------------------------------------------------- /tests/ci/vcpkg-overlay/custom-ports2/nlohmann-json/portfile.cmake: -------------------------------------------------------------------------------- 1 | vcpkg_from_github( 2 | OUT_SOURCE_PATH SOURCE_PATH 3 | REPO nlohmann/json 4 | REF v3.10.4 5 | SHA512 f78592db6218165cbc74c10bcba40366f1bfea84405b7ee25fe97a056d5b7a15aeeb956d93296673928dcbd6e26ffcfb152f885b4a44d5d55751396ccf090835 6 | HEAD_REF master 7 | ) 8 | 9 | vcpkg_cmake_configure( 10 | SOURCE_PATH ${SOURCE_PATH} 11 | OPTIONS 12 | -DJSON_Install=ON 13 | -DJSON_MultipleHeaders=ON 14 | -DJSON_BuildTests=OFF 15 | ) 16 | vcpkg_cmake_install() 17 | vcpkg_cmake_config_fixup(PACKAGE_NAME "nlohmann_json" CONFIG_PATH "lib/cmake/nlohmann_json") 18 | vcpkg_fixup_pkgconfig() 19 | 20 | vcpkg_replace_string( 21 | "${CURRENT_PACKAGES_DIR}/share/nlohmann_json/nlohmann_jsonTargets.cmake" 22 | "{_IMPORT_PREFIX}/nlohmann_json.natvis" 23 | "{_IMPORT_PREFIX}/share/nlohmann_json/nlohmann_json.natvis" 24 | ) 25 | if(EXISTS ${CURRENT_PACKAGES_DIR}/nlohmann_json.natvis) 26 | file(RENAME 27 | "${CURRENT_PACKAGES_DIR}/nlohmann_json.natvis" 28 | "${CURRENT_PACKAGES_DIR}/share/nlohmann_json/nlohmann_json.natvis" 29 | ) 30 | endif() 31 | 32 | file(REMOVE_RECURSE "${CURRENT_PACKAGES_DIR}/debug/include") 33 | file(REMOVE_RECURSE "${CURRENT_PACKAGES_DIR}/debug/share") 34 | file(REMOVE "${CURRENT_PACKAGES_DIR}/debug/nlohmann_json.natvis") 35 | 36 | # Handle copyright 37 | file(INSTALL "${SOURCE_PATH}/LICENSE.MIT" DESTINATION "${CURRENT_PACKAGES_DIR}/share/${PORT}" RENAME copyright) 38 | 39 | vcpkg_check_features(OUT_FEATURE_OPTIONS FEATURE_OPTIONS 40 | FEATURES 41 | test-feature TEST_FEATURE_WAS_SPECIFIED 42 | ) 43 | 44 | if(NOT TEST_FEATURE_WAS_SPECIFIED) 45 | message(FATAL_ERROR "Test feature was not specified") 46 | endif() 47 | 48 | string(COMPARE EQUAL "${VCPKG_LIBRARY_LINKAGE}" "dynamic" SPDLOG_BUILD_SHARED) -------------------------------------------------------------------------------- /tests/ci/vcpkg-overlay/custom-ports2/nlohmann-json/vcpkg.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nlohmann-json", 3 | "version-semver": "3.10.4", 4 | "description": "JSON for Modern C++", 5 | "homepage": "https://github.com/nlohmann/json", 6 | "dependencies": [ 7 | { 8 | "name": "vcpkg-cmake", 9 | "host": true 10 | }, 11 | { 12 | "name": "vcpkg-cmake-config", 13 | "host": true 14 | } 15 | ], 16 | "features": { 17 | "test-feature": { 18 | "description": "Just a feature to test port overlays" 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /tests/ci/vcpkg-overlay/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main() { auto array = nlohmann::json::parse("[1, 2, 3]"); } -------------------------------------------------------------------------------- /tests/ci/vcpkg/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | project(TestProject) 3 | 4 | message(STATUS "PMM URL is ${PMM_URL}") 5 | include(${PMM_INCLUDE}) 6 | pmm(VCPKG 7 | REVISION 2022.05.10 8 | REQUIRES spdlog 9 | DEBUG 10 | ) 11 | 12 | find_package(spdlog REQUIRED CONFIG) 13 | 14 | add_executable(dummy main.cpp) 15 | target_link_libraries(dummy PRIVATE spdlog::spdlog fmt::fmt) 16 | -------------------------------------------------------------------------------- /tests/ci/vcpkg/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main() { 5 | auto console = spdlog::stdout_color_mt("console"); 6 | console->info("Hello!"); 7 | } -------------------------------------------------------------------------------- /tests/conan.test-project/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.12) 2 | project(TestProject) 3 | 4 | include(${PMM_INCLUDE}) 5 | pmm(CONAN BINCRAFTERS DEBUG) 6 | 7 | include(CTest) 8 | -------------------------------------------------------------------------------- /tests/conan.test-project/conanfile.py: -------------------------------------------------------------------------------- 1 | import conans 2 | 3 | 4 | class MyConanProject(conans.ConanFile): 5 | name = 'MyProject' 6 | version = '1.2.3' 7 | -------------------------------------------------------------------------------- /tests/conftest.py: -------------------------------------------------------------------------------- 1 | from ci.fxt import * 2 | -------------------------------------------------------------------------------- /tests/test_bpt.py: -------------------------------------------------------------------------------- 1 | import json 2 | from ci.fxt import CMakeProjectFactory 3 | 4 | 5 | def test_simple(tmp_project_factory: CMakeProjectFactory) -> None: 6 | proj = tmp_project_factory('meow') 7 | proj.write( 8 | 'CMakeLists.txt', r''' 9 | cmake_minimum_required(VERSION 3.11) 10 | project(MyTestProject) 11 | 12 | include(${PMM_INCLUDE}) 13 | pmm(BPT DEPENDENCIES fmt@8.1.1) 14 | pmm(BPT DEPENDENCIES spdlog@1.10.0) 15 | 16 | add_executable(app app.cpp) 17 | target_link_libraries(app PRIVATE spdlog::spdlog) 18 | ''') 19 | proj.write( 20 | 'app.cpp', r''' 21 | #include 22 | 23 | int main() { 24 | spdlog::info("I am a very simple log message!"); 25 | } 26 | ''') 27 | proj.configure() 28 | proj.build() 29 | 30 | 31 | def test_dep_files(tmp_project_factory: CMakeProjectFactory) -> None: 32 | proj = tmp_project_factory('meow') 33 | proj.write( 34 | 'CMakeLists.txt', r''' 35 | cmake_minimum_required(VERSION 3.11) 36 | project(MyTestProject) 37 | 38 | include(${PMM_INCLUDE}) 39 | pmm(BPT DEP_FILES depends.yaml) 40 | 41 | add_executable(app app.cpp) 42 | target_link_libraries(app PRIVATE spdlog::spdlog) 43 | ''') 44 | proj.write('depends.yaml', json.dumps({'dependencies': ['spdlog@1.10.0', 'fmt@8.1.1']})) 45 | proj.write( 46 | 'app.cpp', r''' 47 | #include 48 | 49 | int main() { 50 | spdlog::info("I am another simple log message"); 51 | } 52 | ''') 53 | proj.configure() 54 | proj.build() 55 | -------------------------------------------------------------------------------- /tests/u18.docker/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:18.04 2 | 3 | RUN apt-get update && \ 4 | env DEBIAN_FRONTEND=noninteractive DEBCONF_NONINTERACIVE_SEEN=true \ 5 | apt-get -y install wget curl python3.6 python3-venv python3.6-venv gcc g++ make \ 6 | perl zip unzip git pkg-config 7 | 8 | RUN wget https://github.com/Kitware/CMake/releases/download/v3.13.0/cmake-3.13.0-Linux-x86_64.sh -nv -O /tmp/cmake.sh 9 | RUN sh /tmp/cmake.sh --exclude-subdir --prefix=/usr/local 10 | 11 | -------------------------------------------------------------------------------- /tests/u20-cm3_20.docker/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:20.04 2 | 3 | RUN apt-get update && \ 4 | env DEBIAN_FRONTEND=noninteractive DEBCONF_NONINTERACIVE_SEEN=true \ 5 | apt-get -y install wget curl python3 python3-venv gcc g++ make \ 6 | perl unzip zip git pkg-config 7 | 8 | RUN wget https://github.com/Kitware/CMake/releases/download/v3.20.0/cmake-3.20.0-Linux-x86_64.sh -nv -O /tmp/cmake.sh 9 | RUN sh /tmp/cmake.sh --exclude-subdir --prefix=/usr/local 10 | -------------------------------------------------------------------------------- /tests/u20.docker/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:20.04 2 | 3 | RUN apt-get update && \ 4 | env DEBIAN_FRONTEND=noninteractive DEBCONF_NONINTERACIVE_SEEN=true \ 5 | apt-get -y install wget curl python3 python3-venv gcc g++ make \ 6 | perl unzip zip git pkg-config 7 | 8 | RUN wget https://github.com/Kitware/CMake/releases/download/v3.13.0/cmake-3.13.0-Linux-x86_64.sh -nv -O /tmp/cmake.sh 9 | RUN sh /tmp/cmake.sh --exclude-subdir --prefix=/usr/local 10 | -------------------------------------------------------------------------------- /tests/vcpkg.test-project/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.12) 2 | project(TestProject) 3 | 4 | include(CTest) 5 | 6 | if("$ENV{PMM_VCPKG_TEST_SKIP}") 7 | message("@ctest-skip@") 8 | return() 9 | endif() 10 | 11 | include(${PMM_INCLUDE}) 12 | pmm( 13 | DEBUG 14 | VCPKG 15 | REVISION 2022.05.10 16 | REQUIRES fmt 17 | ) 18 | 19 | find_package(fmt REQUIRED) 20 | add_executable(my-exe main.cpp) 21 | target_link_libraries(my-exe fmt::fmt) 22 | 23 | add_test(my-exe my-exe) 24 | -------------------------------------------------------------------------------- /tests/vcpkg.test-project/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main() { fmt::print("Hello!"); } 4 | --------------------------------------------------------------------------------