From 66b073ab8d427d8fc06ffd715151f372cdde818c Mon Sep 17 00:00:00 2001 From: crueter Date: Mon, 6 Jul 2026 23:11:39 +0200 Subject: [cmake] CPMUtil rewrite of the day number 852 (#4130) Composite PR for several changes to CPMUtil, most notably: - https://git.crueter.xyz/CMake/CPMUtil/pulls/19 - https://git.crueter.xyz/CMake/CPMUtil/pulls/22 - https://git.crueter.xyz/CMake/CPMUtil/pulls/24 - https://git.crueter.xyz/CMake/CPMUtil/pulls/25 These contain a lot of changes that generally simplify control flow and improve ease-of-use. Read those descriptions and patchsets for more info. Signed-off-by: crueter Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4130 Reviewed-by: CamilleLaVey Reviewed-by: MaranBr --- tools/cpm/cmake/README.md | 3 + tools/cpm/cmake/ScriptUtils.cmake | 341 ++++++++++++++++++++++++++++++++++ tools/cpm/cmake/format.cmake | 11 ++ tools/cpm/cmake/ls.cmake | 24 +++ tools/cpm/cmake/package/add.cmake | 104 +++++++++++ tools/cpm/cmake/package/dir.cmake | 33 ++++ tools/cpm/cmake/package/fetch.cmake | 34 ++++ tools/cpm/cmake/package/hash.cmake | 59 ++++++ tools/cpm/cmake/package/patch.cmake | 155 ++++++++++++++++ tools/cpm/cmake/package/reset.cmake | 34 ++++ tools/cpm/cmake/package/rm.cmake | 31 ++++ tools/cpm/cmake/package/update.cmake | 97 ++++++++++ tools/cpm/cmake/package/url.cmake | 28 +++ tools/cpm/cmake/package/version.cmake | 75 ++++++++ tools/cpm/cmake/package/which.cmake | 36 ++++ tools/cpm/cmake/update.cmake | 33 ++++ tools/cpm/common.sh | 45 ----- tools/cpm/format.sh | 9 - tools/cpm/migrate.sh | 39 ++-- tools/cpm/package.sh | 63 +++++-- tools/cpm/package/add.sh | 151 ++++++++++++++- tools/cpm/package/dir.sh | 57 ------ tools/cpm/package/download.sh | 47 ----- tools/cpm/package/fetch.sh | 44 ----- tools/cpm/package/hash.sh | 91 --------- tools/cpm/package/patch.sh | 169 +---------------- tools/cpm/package/reset.sh | 56 ------ tools/cpm/package/rm.sh | 30 --- tools/cpm/package/update.sh | 174 ----------------- tools/cpm/package/util/fetch.sh | 134 ------------- tools/cpm/package/util/fix-hash.sh | 28 --- tools/cpm/package/util/interactive.sh | 228 ----------------------- tools/cpm/package/util/replace.sh | 11 -- tools/cpm/package/util/url-hash.sh | 7 - tools/cpm/package/util/url.sh | 27 --- tools/cpm/package/vars.sh | 158 ---------------- tools/cpm/package/version.sh | 62 ------- tools/cpm/package/which.sh | 8 - tools/cpmutil.sh | 53 ++++-- 39 files changed, 1355 insertions(+), 1434 deletions(-) create mode 100755 tools/cpm/cmake/README.md create mode 100755 tools/cpm/cmake/ScriptUtils.cmake create mode 100755 tools/cpm/cmake/format.cmake create mode 100755 tools/cpm/cmake/ls.cmake create mode 100644 tools/cpm/cmake/package/add.cmake create mode 100755 tools/cpm/cmake/package/dir.cmake create mode 100755 tools/cpm/cmake/package/fetch.cmake create mode 100755 tools/cpm/cmake/package/hash.cmake create mode 100644 tools/cpm/cmake/package/patch.cmake create mode 100755 tools/cpm/cmake/package/reset.cmake create mode 100755 tools/cpm/cmake/package/rm.cmake create mode 100644 tools/cpm/cmake/package/update.cmake create mode 100755 tools/cpm/cmake/package/url.cmake create mode 100755 tools/cpm/cmake/package/version.cmake create mode 100755 tools/cpm/cmake/package/which.cmake create mode 100755 tools/cpm/cmake/update.cmake delete mode 100755 tools/cpm/common.sh delete mode 100755 tools/cpm/format.sh delete mode 100755 tools/cpm/package/dir.sh delete mode 100755 tools/cpm/package/download.sh delete mode 100755 tools/cpm/package/fetch.sh delete mode 100755 tools/cpm/package/hash.sh delete mode 100755 tools/cpm/package/reset.sh delete mode 100755 tools/cpm/package/rm.sh delete mode 100755 tools/cpm/package/update.sh delete mode 100755 tools/cpm/package/util/fetch.sh delete mode 100755 tools/cpm/package/util/fix-hash.sh delete mode 100755 tools/cpm/package/util/interactive.sh delete mode 100755 tools/cpm/package/util/replace.sh delete mode 100755 tools/cpm/package/util/url-hash.sh delete mode 100755 tools/cpm/package/util/url.sh delete mode 100755 tools/cpm/package/vars.sh delete mode 100755 tools/cpm/package/version.sh delete mode 100755 tools/cpm/package/which.sh (limited to 'tools') diff --git a/tools/cpm/cmake/README.md b/tools/cpm/cmake/README.md new file mode 100755 index 0000000000..2dd6dfcbe6 --- /dev/null +++ b/tools/cpm/cmake/README.md @@ -0,0 +1,3 @@ +# CPMUtil CMake Test Scripts + +Testing out some CMake scripting functionality. diff --git a/tools/cpm/cmake/ScriptUtils.cmake b/tools/cpm/cmake/ScriptUtils.cmake new file mode 100755 index 0000000000..dbd908a1ea --- /dev/null +++ b/tools/cpm/cmake/ScriptUtils.cmake @@ -0,0 +1,341 @@ +#!/usr/bin/env -S cmake -P + +# SPDX-FileCopyrightText: Copyright 2026 crueter +# SPDX-License-Identifier: LGPL-3.0-or-later + +cmake_minimum_required(VERSION 3.31) + +# TODO: Account for CPMConfig.cmake +list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}) +list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/CMakeModules) +set(CPMUTIL_ROOT ${CMAKE_SOURCE_DIR}) + +include(CPMUtil) + +# Parse the JSON object for a given key. +macro(parse_key key) + get_json_object(${key}) + set(JSON_NAME ${key}) + parse_object(${object}) +endmacro() + +# Get a package's effective URL, for an already parsed object +function(get_package_url_object out) + if (${url}) + set(${out} "${url}") + else() + get_package_url(URL_OUT "${out}" + GIT_HOST "${git_host}" + REPO "${repo}" + VERSION "${version}" + ARTIFACT "${artifact}" + PACKAGE "${package}") + endif() + + return(PROPAGATE ${out}) +endfunction() + +# Fetch a package from an already-parsed object. +function(fetch_package_object) + set(optionArgs FORCE) + cmake_parse_arguments(ARG "${optionArgs}" "" "" ${ARGN}) + + if (${url}) + set(pkg_url "${url}") + else() + get_package_url(URL_OUT pkg_url + GIT_HOST "${git_host}" + REPO "${repo}" + VERSION "${version}" + ARTIFACT "${artifact}" + PACKAGE "${package}") + endif() + + if (DEFINED CACHE_PATH_OVERRIDE) + set(cache_path ${CACHE_PATH_OVERRIDE}) + else() + get_cache_path(${package} ${version} cache_path) + endif() + + set(fetch_args + URL "${pkg_url}" + HASH "${hash}" + PATH "${cache_path}" + PATCHES ${patches}) + + if(ARG_FORCE) + list(APPEND fetch_args FORCE) + endif() + + fetch_package(${fetch_args}) +endfunction() + +# Format the cpmfile. Requires one of: jq, python, perl +# If you don't have any of those, sorry not sorry. +# Maybe I should make a shell-based alternative. +function(format_cpmfile) + # jq is the preferred formatter since it's the fastest + cpm_find_program(JQ_EXECUTABLE jq) + if (JQ_EXECUTABLE) + set(command ${JQ_EXECUTABLE} --indent 4 -S .) + else() + # Python is simple and works + find_package(Python 3.5 COMPONENTS Interpreter QUIET) + if (Python_FOUND) + set(command ${Python_EXECUTABLE} -m json.tool + --indent 4 --sort-keys) + else() + # json_pp (part of perl) also works well + cpm_find_program(JSONPP_EXECUTABLE json_pp) + if (JSONPP_EXECUTABLE) + set(json_opts "indent" "indent_length=4" "canonical" + "space_after=1" "space_before=0") + string(JOIN "," json_opts_str ${json_opts}) + + set(command ${JSONPP_EXECUTABLE} -f json -t json -json_opt + "${json_opts_str}") + else() + fatal("Fatal: could not find one of jq, Python, or perl" + "(json_pp). Install one of these packages to use" + "CPMUtil's tooling. If they ARE installed, your" + "CMake installation is broken.") + endif() + endif() + endif() + + get_cpmfile_path(file) + mktempdir(TMP) + set(tmp_file ${TMP}/cpmfile.json) + + execute_process(COMMAND ${command} + INPUT_FILE ${file} + OUTPUT_FILE ${tmp_file}) + + # TODO: error handling, mv, cp? + file(COPY_FILE ${tmp_file} ${file}) + + file(REMOVE_RECURSE ${TMP}) +endfunction() + +# Computes expected SHA512 hash of a package +function(get_package_hash url out) + mktempdir(TMP) + + get_filename_component(filename ${url} NAME) + set(file ${TMP}/${filename}) + + cpm_download("${url}" "${file}") + file(SHA512 ${file} ${out}) + + file(REMOVE_RECURSE ${TMP}) + return(PROPAGATE ${out}) +endfunction() + +# Download and put the content into a variable. +function(cpm_download_var url out) + mktempdir(TMP) + + set(file ${TMP}/tmp) + cpm_download("${url}" "${file}") + file(READ ${file} ${out}) + + file(REMOVE_RECURSE ${TMP}) + return(PROPAGATE ${out}) +endfunction() + +# Check if a URL request succeeds without actually saving anything +function(cpm_url_exists url out) + foreach(i RANGE 5) + file(DOWNLOAD "${url}" STATUS ret LOG log TIMEOUT 10) + list(GET ret 0 code) + + if (code EQUAL 0) + set(${out} TRUE) + break() + else() + if (log MATCHES "HTTP/[0-9.]+ (429|403)") + sleep(5) + continue() + endif() + + set(${out} FALSE) + break() + endif() + endforeach() + + return(PROPAGATE ${out}) +endfunction() + +# Get latest tag for a package. +# Requires an already-parsed object +function(get_latest_tag out) + # TODO: Ci packages + if (NOT repo OR ci) + set(${out} null) + return(PROPAGATE ${out}) + endif() + + # first determine if this is a tag or not + if (git_host STREQUAL github.com) + set(api "https://api.github.com/repos/${repo}") + set(check_endpoint "/git/refs/tags") + else() + set(api "https://${git_host}/api/v1/repos/${repo}") + set(check_endpoint "/tags") + endif() + + # artifacts must only check releases + if ("${artifact}" STREQUAL "") + set(json_key name) + set(endpoint "/tags") + else() + set(json_key tag_name) + set(endpoint "/releases") + set(check_endpoint "/releases/tags") + endif() + + cpm_url_exists("${api}${check_endpoint}/${version}" is_tag) + + if (NOT is_tag) + set(${out} null) + return(PROPAGATE ${out}) + endif() + + # strip out prefixes e.g. boost-, openssl-, v + # NOTE: subrels e.g. -1 at the end may cause issues. + string(REGEX REPLACE "[^0-9.-]" "" t_numeric_version "${version}") + string(REGEX REPLACE "-$|^-" "" t_numeric_version "${numeric_version}") + + # now api req + cpm_download_var("${api}${endpoint}" tags) + + string(JSON len LENGTH ${tags}) + math(EXPR last_index "${len} - 1") + + set(greatest_version_numeric ${t_numeric_version}) + set(greatest_version ${version}) + + foreach(i RANGE ${last_index}) + string(JSON tag_obj GET "${tags}" ${i}) + string(JSON tag_name GET "${tag_obj}" ${json_key}) + + # same as above + string(REGEX REPLACE "[^0-9.-]" "" numeric_tag "${tag_name}") + string(REGEX REPLACE "-$|^-" "" numeric_tag "${numeric_tag}") + + if (numeric_tag VERSION_GREATER_EQUAL greatest_version_numeric) + set(greatest_version_numeric ${numeric_tag}) + set(greatest_version ${tag_name}) + endif() + endforeach() + + # numeric version replacement + if (numeric_version) + set(${out} ${greatest_version_numeric}) + else() + set(${out} ${greatest_version}) + endif() + + return(PROPAGATE ${out}) +endfunction() + +# TODO(crueter): Combine these + +# Update hash and version of a package +# Outputs the updated object +function(modify_package object version hash out) + string(JSON new_object SET "${object}" hash "\"${hash}\"") + string(JSON new_object SET "${new_object}" version "\"${version}\"") + + set(${out} "${new_object}") + return(PROPAGATE ${out}) +endfunction() + +# Update hash and numeric_version of a package +# Outputs the updated object +function(modify_package_numeric object version hash out) + string(JSON new_object SET "${object}" hash "\"${hash}\"") + string(JSON new_object SET "${new_object}" numeric_version "\"${version}\"") + + set(${out} "${new_object}") + return(PROPAGATE ${out}) +endfunction() + + +function(get_cpmfile_keys out) + get_cpmfile_content(object) + + string(JSON len LENGTH ${object}) + + math(EXPR last_index "${len} - 1") + foreach(i RANGE ${last_index}) + string(JSON key MEMBER ${object} ${i}) + list(APPEND ${out} ${key}) + endforeach() + + return(PROPAGATE ${out}) +endfunction() + +function(parse_script_args out) + if(PRINT_USAGE) + usage() + cmake_language(EXIT 0) + endif() + + if(NOT CMAKE_SCRIPT_MODE_FILE OR NOT CMAKE_ARGC) + set(${out} "" PARENT_SCOPE) + return() + endif() + + set(found_script FALSE) + set(idx 0) + + get_filename_component(script_name "${CMAKE_SCRIPT_MODE_FILE}" NAME) + + while(idx LESS CMAKE_ARGC) + if(found_script) + set(arg "${CMAKE_ARGV${idx}}") + if(NOT arg STREQUAL "--") + list(APPEND positional_args ${arg}) + endif() + elseif(CMAKE_ARGV${idx} STREQUAL CMAKE_SCRIPT_MODE_FILE) + set(found_script TRUE) + else() + get_filename_component(arg_name "${CMAKE_ARGV${idx}}" NAME) + if(arg_name STREQUAL script_name) + set(found_script TRUE) + endif() + endif() + math(EXPR idx "${idx} + 1") + endwhile() + + if(ALL_PACKAGES AND NOT NO_ALL) + get_cpmfile_keys(all_keys) + if(NO_CI) + set(filtered_keys) + foreach(key ${all_keys}) + parse_key(${key}) + if(NOT ci) + list(APPEND filtered_keys ${key}) + endif() + endforeach() + set(positional_args ${filtered_keys}) + else() + set(positional_args ${all_keys}) + endif() + endif() + + set(${out} ${positional_args} PARENT_SCOPE) +endfunction() + +# Convert a CMake list to a JSON array +function(list_to_array list out) + set(${out} "[]") + set(idx 0) + foreach(elem ${list}) + string(JSON ${out} SET ${${out}} "${idx}" "\"${elem}\"") + math(EXPR idx "${idx} + 1") + endforeach() + + return(PROPAGATE ${out}) +endfunction() \ No newline at end of file diff --git a/tools/cpm/cmake/format.cmake b/tools/cpm/cmake/format.cmake new file mode 100755 index 0000000000..7eff47655f --- /dev/null +++ b/tools/cpm/cmake/format.cmake @@ -0,0 +1,11 @@ +#!/usr/bin/env -S cmake -P + +# SPDX-FileCopyrightText: Copyright 2026 crueter +# SPDX-License-Identifier: LGPL-3.0-or-later + +cmake_minimum_required(VERSION 3.31) + +include(${CMAKE_CURRENT_LIST_DIR}/ScriptUtils.cmake) + +# TODO: Run some sanity checks e.g. patches exist, etc. +format_cpmfile() diff --git a/tools/cpm/cmake/ls.cmake b/tools/cpm/cmake/ls.cmake new file mode 100755 index 0000000000..41a1d1d7af --- /dev/null +++ b/tools/cpm/cmake/ls.cmake @@ -0,0 +1,24 @@ +#!/usr/bin/env -S cmake -P + +# SPDX-FileCopyrightText: Copyright 2026 crueter +# SPDX-License-Identifier: LGPL-3.0-or-later + +cmake_minimum_required(VERSION 3.31) + +include(${CMAKE_CURRENT_LIST_DIR}/ScriptUtils.cmake) + +set(NO_ALL TRUE) + +function(usage) + echo([=[ +Usage: cpmutil.sh ls + +List all packages in the cpmfile. +]=]) +endfunction() + +get_cpmfile_keys(keys) + +foreach(key ${keys}) + echo("${key}") +endforeach() diff --git a/tools/cpm/cmake/package/add.cmake b/tools/cpm/cmake/package/add.cmake new file mode 100644 index 0000000000..02c4e1d900 --- /dev/null +++ b/tools/cpm/cmake/package/add.cmake @@ -0,0 +1,104 @@ +#!/usr/bin/env -S cmake -P + +# SPDX-FileCopyrightText: Copyright 2026 crueter +# SPDX-License-Identifier: LGPL-3.0-or-later + +cmake_minimum_required(VERSION 3.31) + +include(${CMAKE_CURRENT_LIST_DIR}/../ScriptUtils.cmake) + +function(usage) + echo([=[ +Usage: add.cmake [OPTIONS...] + +Internal use only. +]=]) +endfunction() + +if (NOT DEFINED KEY) + fatal("KEY is required") +endif() + +if (NOT DEFINED REPO) + fatal("REPO is required") +endif() + +if (NOT DEFINED VERSION) + fatal("VERSION is required") +endif() + + +if (NOT DEFINED CI) + set(CI FALSE) +endif() + +# construct json +set(object "{}") + +macro(add key val) + set(${key} ${val}) + string(JSON object SET ${object} ${key} "\"${val}\"") +endmacro() + +add(repo ${REPO}) +add(version ${VERSION}) + +if (DEFINED GIT_HOST AND NOT "${GIT_HOST}" STREQUAL github.com) + add(git_host ${GIT_HOST}) +else() + set(git_host github.com) +endif() + +if (DEFINED PACKAGE) + add(package ${PACKAGE}) +else() + set(package ${KEY}) +endif() + +if (DEFINED FIND_ARGS) + add(find_args "${FIND_ARGS}") +endif() + +if (DEFINED MIN_VERSION) + add(min_version ${MIN_VERSION}) +endif() + +if (DEFINED ARTIFACT) + add(artifact ${ARTIFACT}) +endif() + +if (CI) + add(ci true) + + if (DEFINED DISABLED_PLATFORMS) + list_to_array(${DISABLED_PLATFORMS} json_disabled) + string(JSON object SET "${object}" disabled_platforms "${json_disabled}") + endif() +else() + if (DEFINED OPTIONS) + list_to_array(${OPTIONS} json_options) + string(JSON object SET "${object}" options "${json_options}") + endif() + + # get hash + get_package_url_object(pkg_url) + get_package_hash("${pkg_url}" pkg_hash) + + add(hash ${pkg_hash}) + +endif() + +echo("\"${KEY}\": ${object}") + +# now write +get_cpmfile_content(cpmfile) + +# update cached cpmfile content +string(JSON cpmfile SET "${cpmfile}" "${KEY}" "${object}") + +# write cached cpmfile +get_cpmfile_path(file) +file(WRITE ${file} "${cpmfile}") +format_cpmfile() + +echo("-- Added ${KEY}") diff --git a/tools/cpm/cmake/package/dir.cmake b/tools/cpm/cmake/package/dir.cmake new file mode 100755 index 0000000000..bbbf523b31 --- /dev/null +++ b/tools/cpm/cmake/package/dir.cmake @@ -0,0 +1,33 @@ +#!/usr/bin/env -S cmake -P + +# SPDX-FileCopyrightText: Copyright 2026 crueter +# SPDX-License-Identifier: LGPL-3.0-or-later + +cmake_minimum_required(VERSION 3.31) + +include(${CMAKE_CURRENT_LIST_DIR}/../ScriptUtils.cmake) + +function(usage) + echo([=[ +Usage: cpmutil.sh package dir [-a|--all] [PACKAGE]... + +Get the local directory for the specified packages. + +Options: + -a, --all Operate on all packages in this project. +]=]) +endfunction() + +parse_script_args(args) + +# TODO: CI packages. + +foreach(key ${args}) + parse_key(${key}) + + # Guh. + get_cache_path(${package} ${version} cache_path) + + cmake_path(ABSOLUTE_PATH cache_path NORMALIZE OUTPUT_VARIABLE abs_path) + echo("${key}: ${abs_path}") +endforeach() \ No newline at end of file diff --git a/tools/cpm/cmake/package/fetch.cmake b/tools/cpm/cmake/package/fetch.cmake new file mode 100755 index 0000000000..f2b678de01 --- /dev/null +++ b/tools/cpm/cmake/package/fetch.cmake @@ -0,0 +1,34 @@ +#!/usr/bin/env -S cmake -P + +# SPDX-FileCopyrightText: Copyright 2026 crueter +# SPDX-License-Identifier: LGPL-3.0-or-later + +cmake_minimum_required(VERSION 3.31) + +include(${CMAKE_CURRENT_LIST_DIR}/../ScriptUtils.cmake) + +function(usage) + echo([=[ +Usage: cpmutil.sh package fetch [-a|--all] [PACKAGE]... + +Fetch the specified package or packages from their defined download locations. +If the package is already cached, it will not be re-fetched. + +Options: + -a, --all Operate on all packages in this project. +]=]) +endfunction() + +set(NO_CI TRUE) +parse_script_args(args) + +foreach(key ${args}) + if (ci) + continue() + endif() + + parse_key(${key}) + echo("-- ${key}") + + fetch_package_object() +endforeach() diff --git a/tools/cpm/cmake/package/hash.cmake b/tools/cpm/cmake/package/hash.cmake new file mode 100755 index 0000000000..6be23c9882 --- /dev/null +++ b/tools/cpm/cmake/package/hash.cmake @@ -0,0 +1,59 @@ +#!/usr/bin/env -S cmake -P + +# SPDX-FileCopyrightText: Copyright 2026 crueter +# SPDX-License-Identifier: LGPL-3.0-or-later + +cmake_minimum_required(VERSION 3.31) + +include(${CMAKE_CURRENT_LIST_DIR}/../ScriptUtils.cmake) + +function(usage) + echo([=[ +Usage: cpmutil.sh package hash [-a|--all] [PACKAGE]... + +Check the hash of a specific package or packages. +If a hash mismatch occurs, this script will update the package's hash. + +Options: + -a, --all Operate on all packages in this project. + +Note that this procedure will usually take a long time +depending on the number and size of dependencies. +]=]) +endfunction() + +set(NO_CI TRUE) +parse_script_args(args) + +get_cpmfile_content(cpmfile) +foreach(key ${args}) + if (ci) + continue() + endif() + + parse_key("${key}") + echo("-- ${key}") + + get_package_url_object(pkg_url) + get_package_hash("${pkg_url}" pkg_hash) + + if (pkg_hash STREQUAL hash) + echo("Hashes match") + else() + echo_error("Hash mismatch") + echo_error("Expected: ${hash}") + echo_error("Got: ${pkg_hash}") + + modify_package("${object}" "${version}" "${pkg_hash}" new_object) + + # update cached cpmfile content + string(JSON cpmfile SET "${cpmfile}" "${key}" "${new_object}") + + echo("Corrected hash for ${key}") + endif() +endforeach() + +# write cached cpmfile +get_cpmfile_path(file) +file(WRITE ${file} "${cpmfile}") +format_cpmfile() diff --git a/tools/cpm/cmake/package/patch.cmake b/tools/cpm/cmake/package/patch.cmake new file mode 100644 index 0000000000..3844921827 --- /dev/null +++ b/tools/cpm/cmake/package/patch.cmake @@ -0,0 +1,155 @@ +#!/usr/bin/env -S cmake -P + +# SPDX-FileCopyrightText: Copyright 2026 crueter +# SPDX-License-Identifier: LGPL-3.0-or-later + +cmake_minimum_required(VERSION 3.31) + +include(${CMAKE_CURRENT_LIST_DIR}/../ScriptUtils.cmake) + +function(usage) + echo([=[ +Usage: cpmutil.sh package patch [PACKAGE] + +Create an in-tree patch for the specified package. +]=]) +endfunction() + +set(NO_ALL TRUE) + +# arg parsing +parse_script_args(args) + +list(LENGTH args arg_len) +if(arg_len GREATER 0) + list(GET args 0 KEY) +endif() + +if (NOT KEY) + fatal("You must provide a key") +endif() + +if (NOT DEFINED DESCRIPTION) + fatal("No description provided") +endif() + +parse_key(${KEY}) +get_cache_path(${package} ${version} local_cache) + +if (NOT EXISTS ${local_cache}) + fatal("${package} is not fetched locally") +endif() + +# get last patch number + 1 +list(LENGTH patches patches_len) +math(EXPR last_index "${patches_len} - 1") +list(GET patches ${last_index} patch) +string(REGEX MATCH "[0-9][0-9][0-9][0-9]" number "${patch}") +math(EXPR new_patchnum "${number} + 1") + +# now pad it for filename usage +set(padded "0000${new_patchnum}") +string(LENGTH ${padded} total_len) +math(EXPR start_index "${total_len} - 4") +string(SUBSTRING ${padded} ${start_index} 4 padded_patch_number) + +# this requires Git +find_package(Git REQUIRED) + +# fetch temporary package +mktempdir(TMP) + +set(CACHE_PATH_OVERRIDE ${TMP}/local) +fetch_package_object() + +# make patch dir +set(patch_dir "${CPMUTIL_PATCH_DIR}/${KEY}") +file(MAKE_DIRECTORY "${patch_dir}") + +# git stuff +macro(git_cmd out out_status) + echo("${GIT_EXECUTABLE} ${ARGN}") + # TODO: error handling + execute_process(COMMAND ${GIT_EXECUTABLE} ${ARGN} + WORKING_DIRECTORY ${CACHE_PATH_OVERRIDE} + OUTPUT_VARIABLE ${out} + RESULT_VARIABLE ${out_status}) +endmacro() + +# initialize +git_cmd(_ _ init) +git_cmd(_ _ add -A) +git_cmd(_ _ commit -m init) +git_cmd(_ _ "--work-tree=${local_cache}" add -A) + +# check for diffs +git_cmd(_ diff_status diff --cached --quiet) +git_cmd(out _ diff) + +if (diff_status EQUAL 0) + echo(${out}) + echo_error("No differences found between local copy and source") + file(REMOVE_RECURSE ${TMP}) + cmake_language(EXIT 1) +endif() + +# prompt for patch description +git_cmd(_ commit_status commit -m "${DESCRIPTION}") + +# format patch +git_cmd(patch_content _ format-patch -1 HEAD --stdout) + +# now get patch name... + +# strip out existing numeric prefix +string(REGEX REPLACE "^[0-9][0-9][0-9][0-9]-" "" name_part "${DESCRIPTION}") +# spaces to dashes +string(REPLACE " " "-" name_part "${name_part}") +# strip out non-alphanumeric or dash characters +string(REGEX REPLACE "[^a-zA-Z0-9-]" "" name_part "${name_part}") +# collapse consecutive dashes +string(REGEX REPLACE "-+" "-" name_part "${name_part}") +# strip leading/trailing dashes +string(REGEX REPLACE "^-|-$" "" name_part "${name_part}") + +if (NOT name_part) + set(name_part "patch") + echo_error("Warning: could not determine patch name") +endif() + +# Truncate to 49 chars (60 - num prefix - `.patch` suffix) +string(SUBSTRING "${name_part}" 0 49 name_part) + +# And remove any trailing dashes +string(REGEX REPLACE "-$" "" name_part "${name_part}") + +# now construct and save patch +set(patch_name "${padded_patch_number}-${name_part}.patch") + +file(WRITE "${patch_dir}/${patch_name}" "${patch_content}") + +echo("-- Patch created at ${patch_dir}/${patch_name}") +file(REMOVE_RECURSE ${TMP}) + +# Now add to cpmfile. +get_cpmfile_content(content) + +# Build new patches JSON array +set(new_patches "[") +if(patches) + string(JSON existing GET "${content}" "${KEY}" "patches") + string(JSON len LENGTH "${existing}") + math(EXPR range_end "${len} - 1") + foreach(idx RANGE ${range_end}) + string(JSON val GET "${existing}" ${idx}) + string(APPEND new_patches "\"${val}\",") + endforeach() +endif() +string(APPEND new_patches "\"${patch_name}\"]") + +# Update the cpmfile +string(JSON content SET "${content}" "${KEY}" patches "${new_patches}") +get_cpmfile_path(file_path) +file(WRITE "${file_path}" "${content}") +format_cpmfile() + diff --git a/tools/cpm/cmake/package/reset.cmake b/tools/cpm/cmake/package/reset.cmake new file mode 100755 index 0000000000..2b210e4f64 --- /dev/null +++ b/tools/cpm/cmake/package/reset.cmake @@ -0,0 +1,34 @@ +#!/usr/bin/env -S cmake -P + +# SPDX-FileCopyrightText: Copyright 2026 crueter +# SPDX-License-Identifier: LGPL-3.0-or-later + +cmake_minimum_required(VERSION 3.31) + +include(${CMAKE_CURRENT_LIST_DIR}/../ScriptUtils.cmake) + +function(usage) + echo([=[ +Usage: cpmutil.sh package reset [-a|--all] [PACKAGE]... + +Reset a locally fetched package to its original state. +This is most useful for dropping any changes you've made. + +Options: + -a, --all Operate on all packages in this project. +]=]) +endfunction() + +set(NO_CI TRUE) +parse_script_args(args) + +foreach(key ${args}) + if (ci) + continue() + endif() + + parse_key(${key}) + echo("-- ${key}") + + fetch_package_object(FORCE) +endforeach() diff --git a/tools/cpm/cmake/package/rm.cmake b/tools/cpm/cmake/package/rm.cmake new file mode 100755 index 0000000000..2547758920 --- /dev/null +++ b/tools/cpm/cmake/package/rm.cmake @@ -0,0 +1,31 @@ +#!/usr/bin/env -S cmake -P + +# SPDX-FileCopyrightText: Copyright 2026 crueter +# SPDX-License-Identifier: LGPL-3.0-or-later + +cmake_minimum_required(VERSION 3.31) + +include(${CMAKE_CURRENT_LIST_DIR}/../ScriptUtils.cmake) + +function(usage) + echo([=[ +Usage: cpmutil.sh package rm [PACKAGE]... + +Delete a package or packages' cpmfile definition. +]=]) +endfunction() + +set(NO_ALL TRUE) +parse_script_args(args) + +get_cpmfile_content(object) + +# Remove key +foreach(key ${args}) + string(JSON object REMOVE "${object}" ${key}) +endforeach() + +# write +get_cpmfile_path(file) +file(WRITE ${file} "${object}") +format_cpmfile() \ No newline at end of file diff --git a/tools/cpm/cmake/package/update.cmake b/tools/cpm/cmake/package/update.cmake new file mode 100644 index 0000000000..82ea70340a --- /dev/null +++ b/tools/cpm/cmake/package/update.cmake @@ -0,0 +1,97 @@ +#!/usr/bin/env -S cmake -P + +# SPDX-FileCopyrightText: Copyright 2026 crueter +# SPDX-License-Identifier: LGPL-3.0-or-later + +cmake_minimum_required(VERSION 3.31) + +include(${CMAKE_CURRENT_LIST_DIR}/../ScriptUtils.cmake) + +function(usage) + echo([=[ +Usage: cpmutil.sh package update [-a|--all] [-c|--commit] [PACKAGE]... + +Check for updates for a package or packages. + +Options: + -a, --all Operate on all packages in this project. + -c, --commit Automatically generate a commit message +]=]) +endfunction() + +set(NO_CI TRUE) + +parse_script_args(args) + +get_cpmfile_content(cpmfile) +set(update_log "") +set(changed FALSE) + +foreach(key ${args}) + parse_key(${key}) + + get_latest_tag(tag) + + if (${tag} STREQUAL null) + continue() + elseif(NOT ${tag} STREQUAL ${version}) + if (numeric_version) + set(old_version ${numeric_version}) + echo("${key}: ${old_version} -> ${tag}") + set(numeric_version ${tag}) + get_json_element("${object}" version version) + else() + set(old_version ${version}) + echo("${key}: ${old_version} -> ${tag}") + set(version ${tag}) + endif() + + get_json_element("${object}" artifact artifact) + + # Redo version replacements + process_version_replacements() + + get_package_url_object(pkg_url) + get_package_hash("${pkg_url}" pkg_hash) + + if (numeric_version) + modify_package_numeric("${object}" + "${tag}" "${pkg_hash}" new_object) + else() + modify_package("${object}" "${tag}" "${pkg_hash}" new_object) + endif() + + # update cached cpmfile content + string(JSON cpmfile SET "${cpmfile}" "${key}" "${new_object}") + + # used for commit + string(APPEND update_log "* ${key}: ${old_version} -> ${tag}\n") + set(changed TRUE) + else() + echo("${key}: Up to date") + endif() +endforeach() + +get_cpmfile_path(file) +file(WRITE ${file} "${cpmfile}") +format_cpmfile() + +if(MAKE_COMMIT AND changed) + mktempdir(TMP) + + find_package(Git QUIET) + + if (NOT Git_FOUND) + fatal("Git is required to be installed for --commit," + "but it could not be found.") + endif() + + set(msg_file ${TMP}/commit_msg.txt) + file(WRITE ${msg_file} "Update dependencies\n\n${update_log}") + execute_process(COMMAND ${GIT_EXECUTABLE} add cpmfile.json + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) + execute_process(COMMAND ${GIT_EXECUTABLE} commit -F ${msg_file} + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) + + file(REMOVE_RECURSE ${TMP}) +endif() diff --git a/tools/cpm/cmake/package/url.cmake b/tools/cpm/cmake/package/url.cmake new file mode 100755 index 0000000000..cfc1d41037 --- /dev/null +++ b/tools/cpm/cmake/package/url.cmake @@ -0,0 +1,28 @@ +#!/usr/bin/env -S cmake -P + +# SPDX-FileCopyrightText: Copyright 2026 crueter +# SPDX-License-Identifier: LGPL-3.0-or-later + +cmake_minimum_required(VERSION 3.31) + +include(${CMAKE_CURRENT_LIST_DIR}/../ScriptUtils.cmake) + +function(usage) + echo([=[ +Usage: cpmutil.sh package url [-a|--all] [PACKAGE]... + +Get the download URL for the specified packages. + +Options: + -a, --all Operate on all packages in this project. +]=]) +endfunction() + +parse_script_args(args) + +foreach(key ${args}) + parse_key(${key}) + + get_package_url_object(pkg_url) + echo("${key}: ${pkg_url}") +endforeach() \ No newline at end of file diff --git a/tools/cpm/cmake/package/version.cmake b/tools/cpm/cmake/package/version.cmake new file mode 100755 index 0000000000..6ce42a06cd --- /dev/null +++ b/tools/cpm/cmake/package/version.cmake @@ -0,0 +1,75 @@ +#!/usr/bin/env -S cmake -P + +# SPDX-FileCopyrightText: Copyright 2026 crueter +# SPDX-License-Identifier: LGPL-3.0-or-later + +cmake_minimum_required(VERSION 3.31) + +include(${CMAKE_CURRENT_LIST_DIR}/../ScriptUtils.cmake) + +function(usage) + echo([=[ +Usage: cpmutil.sh package version [PACKAGE] [VERSION] + +Update a package's version. If the package uses a sha, you must provide a sha, +and if the package uses a tag, you must provide the fully qualified tag. +]=]) +endfunction() + +set(NO_ALL TRUE) +# arg parsing +parse_script_args(args) + +list(LENGTH args arg_len) +if(arg_len GREATER 0) + list(GET args 0 KEY) +endif() + +if(arg_len GREATER 1) + list(GET args 1 NEW_VERSION) +endif() + +# checks +if (NOT KEY) + fatal("You must provide a key") +endif() + +if (NOT NEW_VERSION) + fatal("You must provide a version") +endif() + +# action +get_cpmfile_content(cpmfile) +parse_key("${KEY}") + +if (numeric_version) + set(numeric_version ${NEW_VERSION}) + get_json_element("${object}" version version) +else() + set(version ${NEW_VERSION}) +endif() + +get_json_element("${object}" artifact artifact) + +# Redo version replacements +process_version_replacements() + +get_package_url_object(pkg_url) +get_package_hash("${pkg_url}" pkg_hash) + +if (numeric_version) + modify_package_numeric("${object}" + "${NEW_VERSION}" "${pkg_hash}" new_object) +else() + modify_package("${object}" "${NEW_VERSION}" "${pkg_hash}" new_object) +endif() + +# update cached cpmfile content +string(JSON cpmfile SET "${cpmfile}" "${key}" "${new_object}") + +# write cached cpmfile +get_cpmfile_path(file) +file(WRITE ${file} "${cpmfile}") +format_cpmfile() + +echo("-- * Updated") diff --git a/tools/cpm/cmake/package/which.cmake b/tools/cpm/cmake/package/which.cmake new file mode 100755 index 0000000000..46c5dcc021 --- /dev/null +++ b/tools/cpm/cmake/package/which.cmake @@ -0,0 +1,36 @@ +#!/usr/bin/env -S cmake -P + +# SPDX-FileCopyrightText: Copyright 2026 crueter +# SPDX-License-Identifier: LGPL-3.0-or-later + +cmake_minimum_required(VERSION 3.31) + +include(${CMAKE_CURRENT_LIST_DIR}/../ScriptUtils.cmake) + +function(usage) + echo([=[ +Usage: cpmutil.sh package which [PACKAGE]... + +Check if a package or packages are defined in the cpmfile. +]=]) +endfunction() + +set(NO_ALL TRUE) +parse_script_args(args) +set(exit 0) + +get_cpmfile_content(object) + +foreach(key ${args}) + # Check if a key exists + string(JSON member ERROR_VARIABLE err GET "${object}" ${key}) + + if (NOT err) + echo("${key}") + else() + echo_error("${key} not defined in cpmfile") + set(exit 1) + endif() +endforeach() + +cmake_language(EXIT ${exit}) \ No newline at end of file diff --git a/tools/cpm/cmake/update.cmake b/tools/cpm/cmake/update.cmake new file mode 100755 index 0000000000..79c1fa1d00 --- /dev/null +++ b/tools/cpm/cmake/update.cmake @@ -0,0 +1,33 @@ +#!/usr/bin/env -S cmake -P + +# SPDX-FileCopyrightText: Copyright 2026 crueter +# SPDX-License-Identifier: LGPL-3.0-or-later + +cmake_minimum_required(VERSION 3.31) + +include(${CMAKE_CURRENT_LIST_DIR}/ScriptUtils.cmake) + +# Update CPMUtil and its tooling/etc +set(pwd ${CMAKE_SOURCE_DIR}) + +set(host "https://git.crueter.xyz") +set(repo "CMake/CPMUtil") +set(release "releases/download/continuous") +set(filename CPMUtil.tar.zst) + +mktempdir(TMP) + +# Download tarball +set(url "${host}/${repo}/${release}/${filename}") +set(file ${TMP}/${filename}) + +cpm_download(${url} ${file}) + +# Extract to current working directory +file(ARCHIVE_EXTRACT + INPUT ${file} + DESTINATION ${pwd}) + +# done :) +echo("Updated CPMUtil") +file(REMOVE_RECURSE ${TMP}) \ No newline at end of file diff --git a/tools/cpm/common.sh b/tools/cpm/common.sh deleted file mode 100755 index 09da6358be..0000000000 --- a/tools/cpm/common.sh +++ /dev/null @@ -1,45 +0,0 @@ -#!/bin/sh -e - -# SPDX-FileCopyrightText: Copyright 2026 crueter -# SPDX-License-Identifier: LGPL-3.0-or-later - -: "${CPM_SOURCE_CACHE:=$PWD/.cache/cpm}" -: "${CPMUTIL_PATCH_DIR:=$PWD/.patch}" - -# TODO: cache cpmfile defs? - -cmd_exists() { - command -v "$1" >/dev/null 2>&1 -} - -must_install() { - for cmd in "$@"; do - cmd_exists "$cmd" || { echo "-- $cmd must be installed" && exit 1; } - done -} - -# Random integer between 100000 and 999999 -_randint() { - awk 'BEGIN { srand(); print int(100000 + rand() * 900000) }' -} - -# Use mktemp if available, use a local temp dir otherwise -make_temp_dir() { - if cmd_exists mktemp; then - mktemp -d - else - TMP="$PWD/.cpm/tmp-$(_randint)" - mkdir -p "$TMP" - echo "$TMP" - fi -} - -# must_install jq find mktemp tar 7z unzip sha512sum git patch curl - -if [ ! -s cpmfile.json ]; then - # TODO: actually make it a no-op - echo "-- Warning: cpmfile.json does not exist or is empty, most commands will be no-ops" -else - LIBS=$(jq -j 'keys_unsorted | join(" ")' cpmfile.json) - export LIBS -fi diff --git a/tools/cpm/format.sh b/tools/cpm/format.sh deleted file mode 100755 index c1452c4cb2..0000000000 --- a/tools/cpm/format.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/sh -e - -# SPDX-FileCopyrightText: Copyright 2026 crueter -# SPDX-License-Identifier: LGPL-3.0-or-later - -jq --indent 4 -S cpmfile.json.new -mv cpmfile.json.new cpmfile.json - -# TODO: Run some sanity checks e.g. patches exist, etc. diff --git a/tools/cpm/migrate.sh b/tools/cpm/migrate.sh index 8be2b1786c..fd4d6880db 100755 --- a/tools/cpm/migrate.sh +++ b/tools/cpm/migrate.sh @@ -6,14 +6,22 @@ SUBMODULES="$(git submodule status --recursive | cut -c2-)" : "${SUBMODULES:?No submodules defined!}" -tmp=$(mktemp) -printf '{}' >"$tmp" - IFS=" " for i in $SUBMODULES; do - sha=$(echo "$i" | cut -d" " -f1 | cut -c1-10) - ver=$(echo "$i" | cut -d" " -f3 | tr -d '()') + commit=$(echo "$i" -d" " -f1 | cut -c1-10) + short_commit=$(echo "$i" -d" " -f1 | cut -c1-7) + + ref=$(echo "$i" | cut -d" " -f3 | tr -d '()') + + case "$ref" in + # ref == commit, use commit versioning + "$short_commit") version="$commit" ;; + # ref is a branch, use commit versioning + heads/*) version="$commit" ;; + # ref is (probably) a tag, use tag versioning + *) version="$ref" ;; + esac path=$(echo "$i" | cut -d" " -f2) name=$(echo "$path" | awk -F/ '{print $NF}') @@ -21,26 +29,9 @@ for i in $SUBMODULES; do remote=$(git -C "$path" remote get-url origin) host=$(echo "$remote" | cut -d"/" -f3) - [ "$host" != github.com ] || host= repo=$(echo "$remote" | cut -d"/" -f4-5 | cut -d'.' -f1) - entry=$(jq -n --arg name "$name" \ - --arg sha "$sha" \ - --arg ver "$ver" \ - --arg repo "$repo" \ - --arg host "$host" \ - '{ - ($name): { - sha: $sha, - version: $ver, - repo: $repo - } + (if $host != "" then {git_host: $host} else {} end) - }') - - jq --argjson new "$entry" '. + $new' "$tmp" >"${tmp}.new" - mv "$tmp.new" "$tmp" + cmake -DKEY="$name" -DVERSION="$version" -DREPO="$repo" -DGIT_HOST="$host" \ + -P "$CMAKE/package/add.cmake" done - -jq '.' "$tmp" >cpmfile.json -rm -f "$tmp" diff --git a/tools/cpm/package.sh b/tools/cpm/package.sh index bf2f3feaa7..8246222d50 100755 --- a/tools/cpm/package.sh +++ b/tools/cpm/package.sh @@ -12,17 +12,17 @@ Usage: cpmutil.sh package [command] Operate on a package or packages. Commands: - hash Verify the hash of a package, and update it if needed - update Check for updates for a package - fetch Fetch a package and place it in the cache - add Add a new package - rm Remove a package - version Change the version of a package - which Check if a package is defined - download Get the download URL for a package - dir Get the local directory for a package - reset Reset a fetched package to its original state - patch Create an in-tree patch based on local modifications + hash Verify the hash of a package, and update it if needed + update Check for updates for a package + fetch Fetch a package and place it in the cache + add Add a new package + rm Remove a package + version Change the version of a package + which Check if a package is defined + url Get the download URL for a package + dir Get the local directory for a package + reset Reset a fetched package to its original state + patch Add a patch to a package in the cpmfile EOF @@ -30,11 +30,48 @@ EOF } SCRIPTS=$(CDPATH='' cd -- "$(dirname -- "$0")/package" && pwd) -export SCRIPTS +CMAKE="$CMAKE/package" + +export SCRIPTS CMAKE + +filter_args() { + print_usage=false + all_flag=false + commit_flag=false + filtered="" + + for arg in "$@"; do + case "$arg" in + -h | --help) print_usage=true ;; + -a | --all) all_flag=true ;; + -c | --commit) commit_flag=true ;; + -ac | -ca) + commit_flag=true + all_flag=true + ;; + *) filtered="$filtered $arg" ;; + esac + done +} while :; do case "$1" in - hash | update | fetch | add | rm | version | which | download | reset | patch | dir) + dir | fetch | hash | reset | rm | url | version | which | update) + cmd="$1" + shift + + filter_args "$@" + cmake_defines="" + + if $print_usage; then cmake_defines="$cmake_defines -DPRINT_USAGE=TRUE"; fi + if $all_flag; then cmake_defines="$cmake_defines -DALL_PACKAGES=TRUE"; fi + if $commit_flag; then cmake_defines="$cmake_defines -DMAKE_COMMIT=TRUE"; fi + + # shellcheck disable=SC2086 + cmake $cmake_defines -P "$CMAKE/$cmd.cmake" -- $filtered + break + ;; + add | patch) cmd="$1" shift "$SCRIPTS/$cmd".sh "$@" diff --git a/tools/cpm/package/add.sh b/tools/cpm/package/add.sh index 8531f524fb..0129613931 100755 --- a/tools/cpm/package/add.sh +++ b/tools/cpm/package/add.sh @@ -49,6 +49,153 @@ done [ -n "$PKG" ] || die "You must specify a package name." -export PKG +# This reads a single-line input from the user and also gives them +# help if needed. +# $1: The prompt itself, without any trailing spaces or whatever +# $2: The help text that gets shown when the user types a question mark +# $3: This is set to "required" if it's necessary, +# otherwise it can continue without input. +# Stores its output in the "reply" variable +read_single() { + while :; do + printf -- "-- %s" "$1" + [ -z "$2" ] || printf " (? for help, %s)" "$3" + printf ": " + if ! IFS= read -r reply; then + echo + [ "$3" = "required" ] && continue || reply="" + fi + case "$reply" in + "?") echo "$2" ;; + "") [ "$3" = "required" ] && continue || return 0 ;; + *) return 0 ;; + esac + done +} + +# read_single, but optional +optional() { + read_single "$1" "$2" "optional" +} + +# a +required() { + read_single "$1" "$2" "required" +} + +# Basically the same as the single line function except multiline, +# also it's never "required" so we don't need that handling. +multi() { + echo "-- $1" + if [ -n "$2" ]; then + echo "-- (? on first line for help, Ctrl-D to finish)" + else + echo "-- (Ctrl-D to finish)" + fi + while :; do + reply=$(cat) + if [ "$(echo "$reply" | head -n 1)" = "?" ] && [ -n "$2" ]; then + echo "$2" + continue + fi + + # removes trailing EOF and empty lines + reply=$(printf '%s\n' "$reply" | + sed 's/\x04$//' | + sed '/^[[:space:]]*$/d') + + break + done +} + +# the actual inputs :) + +required "Package repository (owner/repo)" \ + "The remote repository this is stored on. +You shouldn't include the host, just owner/repo is enough." + +REPO="$reply" + +required "Version of the bundled package" \ + "The tag or commit hash of the bundled package." + +VERSION="$reply" + +optional "Package name for find_package" \ + "When searching for system packages, this argument will be passed to find_package. +For example, using \"Boost\" here will result in CPMUtil internally calling find_package(Boost). +If unset, defaults to the JSON key." + +PACKAGE="$reply" + +optional "Minimum required version" \ + "The minimum required version for this package if it's pulled in by the system." + +MIN_VERSION="$reply" + +optional "Additional find_package arguments, space-separated" \ + "Extra arguments passed to find_package(), (e.g. CONFIG)" + +FIND_ARGS="$reply" + +optional "Git host (default: github.com)" \ + "The hostname of the Git server, if not GitHub (e.g. codeberg.org, git.crueter.xyz)" + +GIT_HOST="$reply" + +optional "Is this a CI package? [y/N]" \ + "Yes if the package is a prebuilt binary distribution (e.g. crueter-ci), +no if the package is built from source if it's bundled." + +case "$reply" in +[Yy]*) CI=true ;; +*) CI=false ;; +esac + +if [ "$CI" = "false" ]; then + optional "Name of the release artifact to download, if applicable. +-- %VERSION% is replaced by the version ($VERSION)" \ + "Download the specified artifact from the release with the previously specified tag." + + ARTIFACT="$reply" + + multi "Fixed options, one per line (e.g. OPUS_BUILD_TESTING OFF)" \ + "Fixed options passed to the project's CMakeLists.txt. Variadic options +should be set in CMake with AddJsonPackage's OPTIONS parameter." + + OPTIONS="$reply" +else + required "Name of the CI artifact" \ + "CI artifacts are stored as --.tar.zst. This option controls the name." + + ARTIFACT="$reply" + + multi "Platforms without a package (one per line)" \ + "Valid platforms: +windows-amd64 windows-arm64 +mingw-amd64 mingw-arm64 +android-aarch64 android-x86_64 +linux-amd64 linux-aarch64 +macos-universal ios-aarch64" + + DISABLED_PLATFORMS="$reply" +fi + +# invoke add.cmake for validation +set -- "$@" \ + -DKEY="$PKG" \ + -DREPO="$REPO" \ + -DVERSION="$VERSION" \ + -DCI="$CI" + +[ -z "$PACKAGE" ] || set -- "$@" -DPACKAGE="$PACKAGE" +[ -z "$GIT_HOST" ] || set -- "$@" -DGIT_HOST="$GIT_HOST" +[ -z "$MIN_VERSION" ] || set -- "$@" -DMIN_VERSION="$MIN_VERSION" +[ -z "$FIND_ARGS" ] || set -- "$@" -DFIND_ARGS="$FIND_ARGS" +[ -z "$OPTIONS" ] || set -- "$@" -DOPTIONS="$(echo "$OPTIONS" | tr '\n' ';')" +[ -z "$ARTIFACT" ] || set -- "$@" -DARTIFACT="$ARTIFACT" +[ -z "$DISABLED_PLATFORMS" ] || set -- "$@" -DDISABLED_PLATFORMS="$(echo "$DISABLED_PLATFORMS" | tr '\n' ';')" + +cmake "$@" -P "$CMAKE"/add.cmake -"$SCRIPTS"/util/interactive.sh +echo "Added package $PKG to cpmfile.json. Include it in your project with AddJsonPackage($PKG)" diff --git a/tools/cpm/package/dir.sh b/tools/cpm/package/dir.sh deleted file mode 100755 index 82a220509d..0000000000 --- a/tools/cpm/package/dir.sh +++ /dev/null @@ -1,57 +0,0 @@ -#!/bin/sh -e - -# SPDX-FileCopyrightText: Copyright 2026 crueter -# SPDX-License-Identifier: LGPL-3.0-or-later - -# shellcheck disable=SC1091 -. "$SCRIPTS"/../common.sh - -usage() { - cat <&2 - RETURN=1 usage -} - -while :; do - case "$1" in - -h | --help) usage ;; - "$0") break ;; - "") break ;; - *) - if [ -n "$PACKAGE" ]; then - die "You may only specify one package." - else - PACKAGE="$1" - fi - ;; - esac - - shift -done - -[ -n "$PACKAGE" ] || usage - -unset JSON -export PACKAGE - -# shellcheck disable=SC1091 -. "$SCRIPTS"/vars.sh - -if [ "$CI" = true ]; then - die "CI packages do not support in-tree patching" -fi - -patch_dir="$PWD/.patch/$PACKAGE" -TMP=$(make_temp_dir) - -tmp_package="${TMP}/${LOWER_PACKAGE}/${KEY}" - -# First fetch the package... - -# shellcheck disable=SC1091 -. "$SCRIPTS"/util/fetch.sh - -local_dir="$CPM_SOURCE_CACHE/$LOWER_PACKAGE/$KEY" - -if [ ! -d "$local_dir" ]; then - echo "-- $PACKAGE_NAME is not fetched locally" >&2 +printf -- "-- Enter a patch description: " +if ! read -r description; then + echo "" >&2 + echo "-- Patch cancelled" >&2 + rm -rf "$TMP" exit 1 fi -src_dir="$tmp_package/$LOWER_PACKAGE/$KEY" - -CPM_SOURCE_CACHE="$tmp_package" fetch_package - -mkdir -p "$patch_dir" - -existing=$(find "$patch_dir" -maxdepth 1 -type f -name '[0-9][0-9][0-9][0-9]-*.patch' 2>/dev/null | sort | tail -1) -if [ -n "$existing" ]; then - last_num=$(basename "$existing" | cut -c1-4) - last_num=$(echo "$last_num" | sed 's/^0*//') - last_num="${last_num:-0}" - next_num=$(printf "%04d" $((last_num + 1))) -else - next_num="0001" -fi - -echo "-- Creating patch for $PACKAGE..." - -orig_dir="$PWD" -if cmd_exists git; then - cd "$src_dir" - - git init >/dev/null 2>&1 - git add -A >/dev/null 2>&1 - git commit -m "base" >/dev/null 2>&1 - - git --work-tree="$local_dir" add -A #>/dev/null 2>&1 - - if git diff --cached --quiet; then - echo "-- No differences found between local and source" - cd "$orig_dir" - rm -rf "$TMP" - exit 0 - fi - - if ! git commit -v; then - echo "-- Patch cancelled" >&2 - cd "$orig_dir" - rm -rf "$TMP" - exit 1 - fi - - description=$(git log -1 --format="%s") - - git format-patch -1 HEAD --stdout >"$patch_dir/tmp.patch" - rm -rf .git - - cd "$orig_dir" - unset orig_dir -else - if diff -ruN "$src_dir" "$local_dir" >/dev/null 2>&1; then - echo "-- No differences found between local and source" - rm -rf "$TMP" - exit 0 - fi - - printf -- "-- Enter a patch description: " - if ! read -r description; then - echo "" >&2 - echo "-- Patch cancelled" >&2 - rm -rf "$TMP" - exit 1 - fi - - mkdir -p "$TMP/patchdir/a" "$TMP/patchdir/b" - cp -r "$src_dir/." "$TMP/patchdir/a/" - cp -r "$local_dir/." "$TMP/patchdir/b/" - cd "$TMP/patchdir" - { - echo "Subject: [PATCH] $description" | fold -s -w 80 - echo "---" - diff -ruN a b || true - } >"$patch_dir/tmp.patch" - - cd "$orig_dir" - unset orig_dir -fi - -rm -rf "$TMP" - -if [ ! -s "$patch_dir/tmp.patch" ]; then - rm -f "$patch_dir/tmp.patch" - echo "-- No differences found between local and source for $PACKAGE" - exit 0 -fi - -name_part=$(printf "%s" "$description" | sed 's/^[0-9]\{4\}-//' | sed 's/ /-/g; s/[^a-zA-Z0-9-]//g; s/--*/-/g; s/^-//; s/-$//') -[ -n "$name_part" ] || name_part="patch" - -# max is 60 chars, minus 5 for number, minus 6 for patch suffix -max_name_len=49 -name_part=$(printf "%s" "$name_part" | cut -c1-"$max_name_len" | sed 's/-$//') - -patch_name="${next_num}-${name_part}.patch" - -mv "$patch_dir/tmp.patch" "$patch_dir/$patch_name" - -NEW_JSON=$(echo "$JSON" | jq ".patches += [\"$patch_name\"]") -"$SCRIPTS"/util/replace.sh "$PACKAGE" "$NEW_JSON" - -echo "-- Patch created at $patch_dir/$patch_name" +cmake -DDESCRIPTION="$description" -P "$CMAKE"/patch.cmake "$@" diff --git a/tools/cpm/package/reset.sh b/tools/cpm/package/reset.sh deleted file mode 100755 index 6e98c00d48..0000000000 --- a/tools/cpm/package/reset.sh +++ /dev/null @@ -1,56 +0,0 @@ -#!/bin/sh -e - -# SPDX-FileCopyrightText: Copyright 2026 crueter -# SPDX-License-Identifier: LGPL-3.0-or-later - -# shellcheck disable=SC1091 -. "$SCRIPTS"/../common.sh - -usage() { - cat <cpmfile.json.new - mv cpmfile.json.new cpmfile.json - echo "-- Removed $pkg" -done diff --git a/tools/cpm/package/update.sh b/tools/cpm/package/update.sh deleted file mode 100755 index 96e143f662..0000000000 --- a/tools/cpm/package/update.sh +++ /dev/null @@ -1,174 +0,0 @@ -#!/bin/sh -e - -# SPDX-FileCopyrightText: Copyright 2026 crueter -# SPDX-License-Identifier: LGPL-3.0-or-later - -filter_out() { - TAGS=$(echo "$TAGS" | jq "[.[] | select(.name | test(\"$1\"; \"i\") | not)]") -} - -filter_in() { - TAGS=$(echo "$TAGS" | jq "[.[] | select(.name | test(\"$1\"; \"i\"))]") -} - -usage() { - cat </dev/null 2>&1; then - TAGS=$(gh api --method GET "$endpoint") - elif [ "$GIT_HOST" = github.com ]; then - TAGS=$(curl -sfL "https://api.github.com$endpoint") - else - TAGS=$(curl -sfL "https://$GIT_HOST/api/v1$endpoint") - fi - - # filter out some commonly known annoyances - # TODO add more - - if [ "$PACKAGE" = "vulkan-validation-layers" ]; then - filter_in vulkan-sdk - else - filter_out vulkan-sdk - fi - - filter_out yotta # mbedtls - - filter_out vksc - - # ignore betas/alphas (remove if needed) - filter_out alpha - filter_out beta - filter_out rc - - # Add package-specific overrides here, e.g. here for fmt: - [ "$PACKAGE" != fmt ] || filter_out v0.11 - - # Or for OpenSSL: - if [ "$PACKAGE" = openssl ]; then - filter_out rsaref - filter_in "openssl-" - fi - - LATEST=$(echo "$TAGS" | jq -r '.[0].name') - - if [ "$LATEST" = "null" ] || - { [ "$LATEST" = "$TAG" ] && [ "$FORCE" != "true" ]; }; then - echo "-- * Up-to-date" - continue - fi - - # TODO: This is identical to version.sh - - if [ "$HAS_REPLACE" = "true" ]; then - # this just extracts the tag prefix - VERSION_PREFIX=$(echo "$ORIGINAL_TAG" | cut -d"%" -f1) - - # then we strip out the prefix from the new tag, and make that our new version - if [ -z "$VERSION_PREFIX" ]; then - NEW_VERSION="$LATEST" - else - NEW_VERSION=$(echo "$LATEST" | sed "s/$VERSION_PREFIX//g") - fi - else - NEW_VERSION="$LATEST" - fi - - _commit="$_commit -* $PACKAGE: $VERSION -> $NEW_VERSION" - - echo "-- * Version $LATEST available, current is $TAG" - - if [ "$UPDATE" = "true" ]; then - if [ "$HAS_REPLACE" = "true" ]; then - JSON=$(echo "$JSON" | jq ".version = \"$NEW_VERSION\"") - else - JSON=$(echo "$JSON" | jq ".tag = \"$NEW_VERSION\"") - fi - - echo "-- * -- Updating hash" - - # shellcheck disable=SC1091 - . "$SCRIPTS"/vars.sh - HASH=$("$SCRIPTS"/util/url-hash.sh "$DOWNLOAD") - JSON=$(echo "$JSON" | jq ".hash = \"$HASH\"") - - "$SCRIPTS"/util/replace.sh "$PACKAGE" "$JSON" - fi -done - -if [ "$UPDATE" = "true" ] && [ "$COMMIT" = "true" ] && [ -n "$_commit" ]; then - git add "cpmfile.json" - git commit -m "Update dependencies -$_commit" -fi diff --git a/tools/cpm/package/util/fetch.sh b/tools/cpm/package/util/fetch.sh deleted file mode 100755 index 7db568f3e6..0000000000 --- a/tools/cpm/package/util/fetch.sh +++ /dev/null @@ -1,134 +0,0 @@ -#!/bin/sh -e - -# SPDX-FileCopyrightText: Copyright 2026 crueter -# SPDX-License-Identifier: LGPL-3.0-or-later - -# shellcheck disable=SC1091 -. "$SCRIPTS"/../common.sh - -download_package() { - mkdir -p "$CPM_SOURCE_CACHE" - - tmp=$(make_temp_dir) - - FILENAME=$(basename "$DOWNLOAD") - - OUTFILE="$tmp/$FILENAME" - - OUTDIR="${CPM_SOURCE_CACHE}/${LOWER_PACKAGE}/${KEY}" - if [ -d "$OUTDIR" ]; then return; fi - - curl "$DOWNLOAD" -sS -L -o "$OUTFILE" - - ACTUAL_HASH=$(sha512sum "$OUTFILE" | cut -d" " -f1) - if [ "$ACTUAL_HASH" != "$HASH" ]; then - echo "!! $FILENAME did not match expected hash; expected $HASH but got $ACTUAL_HASH" >&2 - exit 1 - fi - - TMPDIR="$tmp/extracted" - mkdir -p "$OUTDIR" - mkdir -p "$TMPDIR" - - PREVDIR="$PWD" - mkdir -p "$TMPDIR" - cd "$TMPDIR" - - case "$FILENAME" in - *.7z) - must_install 7z - 7z x "$OUTFILE" >/dev/null - ;; - *.tar*) - # TODO: Extensions - must_install tar - tar xf "$OUTFILE" >/dev/null - ;; - *.zip) - must_install unzip - unzip "$OUTFILE" >/dev/null - ;; - esac - - # basically if only one real item exists at the top we just move everything from there - # since github and some vendors hate me - DIRS=$(find . -maxdepth 1 -type d -o -type f) - - # thanks gnu - if [ "$(echo "$DIRS" | wc -l)" -eq 2 ]; then - SUBDIR=$(find . -maxdepth 1 -type d -not -name ".") - mv "$SUBDIR"/* "$OUTDIR" - mv "$SUBDIR"/.* "$OUTDIR" 2>/dev/null || true - rmdir "$SUBDIR" - else - mv ./* "$OUTDIR" - mv ./.* "$OUTDIR" 2>/dev/null || true - fi - - cd "$OUTDIR" - - # TODO: Common custom patch/source cache dirs - if echo "$JSON" | grep -e "patches" >/dev/null; then - PATCHES=$(echo "$JSON" | jq -r '.patches | join(" ")') - for patch in $PATCHES; do - abs_patch="$CPMUTIL_PATCH_DIR/$PACKAGE/$patch" - if [ ! -f "$abs_patch" ]; then - echo "-- * Attempted to apply nonexistent patch $patch!" - continue - fi - - echo "-- * Applying patch $patch" - patch --binary -p1 <"$abs_patch" - done - fi - - cd "$PREVDIR" - - rm -rf "$tmp" -} - -# TODO: individual platform fetch? -ci_package() { - [ "$REPO" != null ] || { echo "-- ! No repo defined" && return; } - mkdir -p "$CPM_SOURCE_CACHE" - - echo "-- CI package $PACKAGE_NAME" - - for platform in \ - windows-amd64 windows-arm64 \ - mingw-amd64 mingw-arm64 \ - android-aarch64 android-x86_64 \ - linux-amd64 linux-aarch64 \ - macos-universal ios-aarch64; do - echo "-- * platform $platform" - - case $DISABLED in - *"$platform"*) - echo "-- * -- disabled" - continue - ;; - esac - - FILENAME="${NAME}-${platform}-${VERSION}.${EXT}" - DOWNLOAD="https://$GIT_HOST/${REPO}/releases/download/v${VERSION}/${FILENAME}" - KEY="$platform-$VERSION" - - OUTDIR="${CPM_SOURCE_CACHE}/${LOWER_PACKAGE}/${KEY}" - [ -d "$OUTDIR" ] && continue - - HASH_URL="${DOWNLOAD}.sha512sum" - - HASH=$(curl "$HASH_URL" -sS -q -L -o -) - - download_package - done -} - -fetch_package() { - if [ "$CI" = "true" ]; then - ci_package - else - echo "-- Downloading regular package $PACKAGE, with key $KEY, from $DOWNLOAD" - download_package - fi -} diff --git a/tools/cpm/package/util/fix-hash.sh b/tools/cpm/package/util/fix-hash.sh deleted file mode 100755 index 476b15af89..0000000000 --- a/tools/cpm/package/util/fix-hash.sh +++ /dev/null @@ -1,28 +0,0 @@ -#!/bin/sh -e - -# SPDX-FileCopyrightText: Copyright 2026 crueter -# SPDX-License-Identifier: LGPL-3.0-or-later - -# shellcheck disable=SC1091 - -: "${PACKAGE:=$1}" - -# re-read json files -# shellcheck disable=SC2016 -. "$SCRIPTS"/vars.sh - -[ "$CI" = null ] || exit 0 - -ACTUAL=$("$SCRIPTS"/util/url-hash.sh "$DOWNLOAD") - -if [ "$ACTUAL" != "$HASH" ] && [ "$QUIET" != true ]; then - echo "-- * Expected $HASH" - echo "-- * Got $ACTUAL" - [ "$UPDATE" != "true" ] && exit 1 -fi - -if [ "$UPDATE" = "true" ] && [ "$ACTUAL" != "$HASH" ]; then - NEW_JSON=$(echo "$JSON" | jq ".hash = \"$ACTUAL\"") - - "$SCRIPTS"/util/replace.sh "$PACKAGE" "$NEW_JSON" -fi diff --git a/tools/cpm/package/util/interactive.sh b/tools/cpm/package/util/interactive.sh deleted file mode 100755 index 1f5a16aa07..0000000000 --- a/tools/cpm/package/util/interactive.sh +++ /dev/null @@ -1,228 +0,0 @@ -#!/bin/sh -e - -# SPDX-FileCopyrightText: Copyright 2026 crueter -# SPDX-License-Identifier: LGPL-3.0-or-later - -# This reads a single-line input from the user and also gives them -# help if needed. -# $1: The prompt itself, without any trailing spaces or whatever -# $2: The help text that gets shown when the user types a question mark -# $3: This is set to "required" if it's necessary, -# otherwise it can continue without input. -# Stores its output in the "reply" variable -read_single() { - while :; do - printf -- "-- %s" "$1" - [ -z "$2" ] || printf " (? for help, %s)" "$3" - printf ": " - if ! IFS= read -r reply; then - echo - [ "$3" = "required" ] && continue || reply="" - fi - case "$reply" in - "?") echo "$2" ;; - "") [ "$3" = "required" ] && continue || return 0 ;; - *) return 0 ;; - esac - done -} - -# read_single, but optional -optional() { - read_single "$1" "$2" "optional" -} - -# a -required() { - read_single "$1" "$2" "required" -} - -# Basically the same as the single line function except multiline, -# also it's never "required" so we don't need that handling. -multi() { - echo "-- $1" - if [ -n "$2" ]; then - echo "-- (? on first line for help, Ctrl-D to finish)" - else - echo "-- (Ctrl-D to finish)" - fi - while :; do - reply=$(cat) - if [ "$(echo "$reply" | head -n 1)" = "?" ] && [ -n "$2" ]; then - echo "$2" - continue - fi - - # removes trailing EOF and empty lines - reply=$(printf '%s\n' "$reply" | - sed 's/\x04$//' | - sed '/^[[:space:]]*$/d') - - break - done -} - -# the actual inputs :) - -required "Package repository (owner/repo)" \ - "The remote repository this is stored on. -You shouldn't include the host, just owner/repo is enough." - -REPO="$reply" - -optional "Package name for find_package" \ - "When searching for system packages, this argument will be passed to find_package. -For example, using \"Boost\" here will result in CPMUtil internally calling find_package(Boost)." - -PACKAGE="$reply" - -optional "Minimum required version" \ - "The minimum required version for this package if it's pulled in by the system." - -MIN_VERSION="$reply" - -optional "Additional find_package arguments, space-separated" \ - "Extra arguments passed to find_package(), (e.g. CONFIG)" - -FIND_ARGS="$reply" - -optional "Git host (default: github.com)" \ - "The hostname of the Git server, if not GitHub (e.g. codeberg.org, git.crueter.xyz)" - -GIT_HOST="$reply" - -required "Numeric version of the bundled package" \ - "The semantic version of the bundled package. This is only used for package identification, -and if you use tag/artifact fetching. Do not input the entire tag here; for example, if you're using -tag v1.3.0, then set this to 1.3.0 and set the tag to v%VERSION%." - -VERSION="$reply" - -optional "Is this a CI package? [y/N]" \ - "Yes if the package is a prebuilt binary distribution (e.g. crueter-ci), -no if the package is built from source if it's bundled." - -case "$reply" in -[Yy]*) CI=true ;; -*) CI=false ;; -esac - -if [ "$CI" = "false" ]; then - while :; do - required "Use tag or commit sha versioning? [tag/sha]" \ - "Tag versioning is compatible with auto-updating. -Use sha versioning for projects with improper tagging practices" - - if [ "$reply" = "tag" ]; then - TAG=1 - break - elif [ "$reply" = "sha" ]; then - SHA=1 - break - else - echo "-- Invalid choice $reply" - fi - done - - if [ "$TAG" = "1" ]; then - optional "Name of the upstream tag. %VERSION% is replaced by the numeric version ($VERSION) (default: %VERSION%)" \ - "Most commonly this will be something like v%VERSION% or release-%VERSION%, or just %VERSION%." - - TAGNAME="$reply" - [ -n "$TAGNAME" ] || TAGNAME="%VERSION%" - - optional "Name of the release artifact to download, if applicable. --- %VERSION% is replaced by the numeric version ($VERSION) and %TAG% is replaced by the tag name" \ - "Download the specified artifact from the release with the previously specified tag. -If unspecified, the source code at the specified tag will be used instead." - - ARTIFACT="$reply" - else - required "Commit sha" \ - "The short Git commit sha to use. You're recommended to keep this short, e.g. 10 characters." - - SHA="$reply" - fi - - multi "Fixed options, one per line (e.g. OPUS_BUILD_TESTING OFF)" \ - "Fixed options passed to the project's CMakeLists.txt. Variadic options -should be set in CMake with AddJsonPackage's OPTIONS parameter." - - OPTIONS="$reply" -else - required "Name of the CI artifact" \ - "CI artifacts are stored as --.tar.zst. This option controls the name." - - ARTIFACT="$reply" - - multi "Platforms without a package (one per line)" \ - "Valid platforms: -windows-amd64 windows-arm64 -mingw-amd64 mingw-arm64 -android-aarch64 android-x86_64 -linux-amd64 linux-aarch64 -macos-universal ios-aarch64" - - DISABLED_PLATFORMS="$reply" -fi - -# now time to construct the actual json -jq_input='{repo: "'"$REPO"'"}' - -# common trivial fields -[ -z "$PACKAGE" ] || jq_input="$jq_input + {package: \"$PACKAGE\"}" -[ -z "$MIN_VERSION" ] || jq_input="$jq_input + {min_version: \"$MIN_VERSION\"}" -[ -z "$FIND_ARGS" ] || jq_input="$jq_input + {find_args: \"$FIND_ARGS\"}" -jq_input="$jq_input + {version: \"$VERSION\"}" - -if [ -n "$GIT_HOST" ] && [ "$GIT_HOST" != "github.com" ]; then - jq_input="$jq_input + {git_host: \"$GIT_HOST\"}" -fi - -if [ "$CI" = "true" ]; then - jq_input="$jq_input + { - ci: true, - artifact: \"$ARTIFACT\" - }" - - # disabled platforms - if [ -n "$DISABLED_PLATFORMS" ] && [ -n "$(printf '%s' "$DISABLED_PLATFORMS" | tr -d ' \t\n\r')" ]; then - disabled_json=$(printf '%s\n' "$DISABLED_PLATFORMS" | jq -R . | jq -s .) - jq_input="$jq_input + {disabled_platforms: $disabled_json}" - fi -else - [ -z "$MIN_VERSION" ] || jq_input="$jq_input + {version: \"$MIN_VERSION\"}" - jq_input="$jq_input + {hash: \"\"}" - - # options - if [ -n "$OPTIONS" ] && [ -n "$(printf '%s' "$OPTIONS" | tr -d ' \t\n\r')" ]; then - options_json=$(printf '%s\n' "$OPTIONS" | jq -R . | jq -s .) - jq_input="$jq_input + {options: $options_json}" - fi - - # versioning stuff - if [ "$TAG" = 1 ]; then - jq_input="$jq_input + {tag: \"$TAGNAME\"}" - [ -z "$ARTIFACT" ] || jq_input="$jq_input + {artifact: \"$ARTIFACT\"}" - else - jq_input="$jq_input + {sha: \"$SHA\"}" - fi -fi - -JSON=$(jq -n "$jq_input") - -# shellcheck disable=SC1091 -. "$SCRIPTS"/vars.sh - -if [ "$CI" != true ]; then - HASH=$("$SCRIPTS"/util/url-hash.sh "$DOWNLOAD") - JSON=$(echo "$JSON" | jq ".hash = \"$HASH\"") -fi - -jq --arg key "$PKG" --argjson new "$JSON" \ - '.[$key] = $new' "cpmfile.json" --indent 4 >"cpmfile.json.tmp" && - mv "cpmfile.json.tmp" cpmfile.json - -"$SCRIPTS"/format.sh - -echo "Added package $PKG to cpmfile.json. Include it in your project with AddJsonPackage($PKG)" diff --git a/tools/cpm/package/util/replace.sh b/tools/cpm/package/util/replace.sh deleted file mode 100755 index 7141747a12..0000000000 --- a/tools/cpm/package/util/replace.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/sh -e - -# SPDX-FileCopyrightText: Copyright 2026 crueter -# SPDX-License-Identifier: LGPL-3.0-or-later - -# Replace a specified package with a modified json. - -jq --indent 4 --argjson repl "$2" ".\"$1\" *= \$repl" cpmfile.json >cpmfile.json.new -mv cpmfile.json.new cpmfile.json - -echo "-- * -- Updated cpmfile.json" diff --git a/tools/cpm/package/util/url-hash.sh b/tools/cpm/package/util/url-hash.sh deleted file mode 100755 index eae784b53d..0000000000 --- a/tools/cpm/package/util/url-hash.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/sh - -# SPDX-FileCopyrightText: Copyright 2025 crueter -# SPDX-License-Identifier: LGPL-3.0-or-later - -SUM=$(curl -Ls "$1" -o - | sha512sum) -echo "$SUM" | cut -d " " -f1 diff --git a/tools/cpm/package/util/url.sh b/tools/cpm/package/util/url.sh deleted file mode 100755 index 22a69bf898..0000000000 --- a/tools/cpm/package/util/url.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/bin/sh -e - -# SPDX-FileCopyrightText: Copyright 2026 crueter -# SPDX-License-Identifier: LGPL-3.0-or-later - -# Get the download URL for a package - -if [ "$URL" != "null" ]; then - DOWNLOAD="$URL" -elif [ "$REPO" != "null" ]; then - GIT_URL="https://$GIT_HOST/$REPO" - - if [ "$SHA" != "null" ]; then - DOWNLOAD="${GIT_URL}/archive/${SHA}.tar.gz" - elif [ "$TAG" != "null" ]; then - if [ "$ARTIFACT" != "null" ]; then - DOWNLOAD="${GIT_URL}/releases/download/${TAG}/${ARTIFACT}" - else - DOWNLOAD="${GIT_URL}/archive/refs/tags/${TAG}.tar.gz" - fi - fi -else - echo "!! No repo or URL defined for $PACKAGE_NAME" - exit 1 -fi - -export DOWNLOAD diff --git a/tools/cpm/package/vars.sh b/tools/cpm/package/vars.sh deleted file mode 100755 index dd497407c8..0000000000 --- a/tools/cpm/package/vars.sh +++ /dev/null @@ -1,158 +0,0 @@ -#!/bin/sh -e - -# SPDX-FileCopyrightText: Copyright 2026 crueter -# SPDX-License-Identifier: LGPL-3.0-or-later - -# shellcheck disable=SC1091 - -value() { - echo "$JSON" | jq -r ".$1" -} - -if [ -z "$JSON" ]; then - [ -n "$PACKAGE" ] || { echo "Package was not specified" && exit 0; } - - # shellcheck disable=SC2153 - JSON=$(jq -r ".\"$PACKAGE\" | select( . != null )" cpmfile.json) - - if [ -z "$JSON" ]; then - echo "!! No cpmfile definition for $PACKAGE" >&2 - exit 1 - fi -fi - -# unset stuff -export PACKAGE_NAME="null" -export REPO="null" -export CI="null" -export GIT_HOST="null" -export EXT="null" -export NAME="null" -export DISABLED="null" -export TAG="null" -export ARTIFACT="null" -export SHA="null" -export VERSION="null" -export MIN_VERSION="null" -export DOWNLOAD="null" -export URL="null" -export KEY="null" -export HASH="null" -export ORIGINAL_TAG="null" -export HAS_REPLACE="null" -export VERSION_REPLACE="null" - -######## -# Meta # -######## - -REPO=$(value "repo") -CI=$(value "ci") - -PACKAGE_NAME=$(value "package") -[ "$PACKAGE_NAME" != null ] || PACKAGE_NAME="$PACKAGE" - -GIT_HOST=$(value "git_host") -[ "$GIT_HOST" != null ] || GIT_HOST=github.com - -# used for cache key -LOWER_PACKAGE=$(echo "$PACKAGE_NAME" | tr '[:upper:]' '[:lower:]') - -export PACKAGE_NAME -export LOWER_PACKAGE -export REPO -export CI -export GIT_HOST - -###################### -# CI Package Parsing # -###################### - -MIN_VERSION=$(value "min_version") -VERSION=$(value "version") - -export VERSION -export MIN_VERSION - -if [ "$CI" = "true" ]; then - EXT=$(value "extension") - [ "$EXT" != null ] || EXT="tar.zst" - - NAME=$(value "name") - DISABLED=$(echo "$JSON" | jq -j '.disabled_platforms') - - [ "$NAME" != null ] || NAME="$PACKAGE_NAME" - - export EXT - export NAME - export DISABLED - - return 0 -fi - -############## -# Versioning # -############## - -TAG=$(value "tag") -ARTIFACT=$(value "artifact") -SHA=$(value "sha") - -if echo "$TAG" | grep -e "%VERSION%" >/dev/null; then - HAS_REPLACE=true -else - HAS_REPLACE=false -fi - -ORIGINAL_TAG="$TAG" - -TAG=$(echo "$TAG" | sed "s/%VERSION%/$VERSION/g") -ARTIFACT=$(echo "$ARTIFACT" | sed "s/%VERSION%/$VERSION/g") -ARTIFACT=$(echo "$ARTIFACT" | sed "s/%TAG%/$TAG/g") - -export TAG -export ARTIFACT -export SHA -export VERSION -export ORIGINAL_TAG -export HAS_REPLACE - -############### -# URL Parsing # -############### - -URL=$(value "url") - -. "$SCRIPTS"/util/url.sh - -export DOWNLOAD - -############### -# Key Parsing # -############### - -if [ "$SHA" != null ]; then - KEY=$(echo "$SHA" | cut -c1-4) -elif [ "$VERSION" != null ]; then - KEY="$VERSION" -elif [ "$TAG" != null ]; then - KEY="$TAG" -else - echo "!! No valid key could be determined for $PACKAGE_NAME. Must define one of: sha, tag, version" - exit 1 -fi - -export KEY - -################ -# Hash Parsing # -################ - -HASH=$(value "hash") - -if [ "$HASH" = null ]; then - echo "!! No hash defined for $PACKAGE_NAME" >&2 -fi - -export HASH -export JSON diff --git a/tools/cpm/package/version.sh b/tools/cpm/package/version.sh deleted file mode 100755 index cfeaa27f1e..0000000000 --- a/tools/cpm/package/version.sh +++ /dev/null @@ -1,62 +0,0 @@ -#!/bin/sh -e - -# SPDX-FileCopyrightText: Copyright 2026 crueter -# SPDX-License-Identifier: LGPL-3.0-or-later - -# shellcheck disable=SC1091 - -usage() { - cat </dev/null 2>&1 diff --git a/tools/cpmutil.sh b/tools/cpmutil.sh index 6c18e6999b..b1edbbde4b 100755 --- a/tools/cpmutil.sh +++ b/tools/cpmutil.sh @@ -7,8 +7,7 @@ ROOTDIR=$(CDPATH='' cd -- "$(dirname -- "$0")/cpm" && pwd) SCRIPTS="$ROOTDIR" - -. "$SCRIPTS"/common.sh +CMAKE="$ROOTDIR/cmake" RETURN=0 @@ -26,33 +25,55 @@ General command-line utility for CPMUtil operations. Commands: package Run operations on a package or packages format Format cpmfile + ls List all packages in the cpmfile update Update CPMUtil and its tooling migrate Convert submodules to a basic cpmfile Package commands: - hash Verify the hash of a package, and update it if needed - update Check for updates for a package - fetch Fetch a package and place it in the cache - add Add a new package - rm Remove a package - version Change the version of a package - which Check if a package is defined - download Get the download URL for a package - dir Get the local directory for a package - reset Reset a fetched package to its original state - patch Create an in-tree patch based on local modifications + hash Verify the hash of a package, and update it if needed + update Check for updates for a package + fetch Fetch a package and place it in the cache + add Add a new package + rm Remove a package + version Change the version of a package + which Check if a package is defined + url Get the download URL for a package + dir Get the local directory for a package + reset Reset a fetched package to its original state + patch Create an in-tree patch based on local modifications EOF exit $RETURN } -export ROOTDIR +export ROOTDIR CMAKE SCRIPTS while :; do case "$1" in - format | update | migrate) - "$SCRIPTS/$1".sh + format | update) + cmake -P "$CMAKE/$1".cmake + break + ;; + ls) + shift + print_usage=false + filtered="" + for arg in "$@"; do + case "$arg" in + -h | --help) print_usage=true ;; + -n) ;; + *) filtered="$filtered $arg" ;; + esac + done + cmake_defines="" + $print_usage && cmake_defines="-DPRINT_USAGE=TRUE" + # shellcheck disable=SC2086 + cmake $cmake_defines -P "$CMAKE/ls.cmake" -- $filtered + break + ;; + migrate) + "$SCRIPTS"/migrate.sh break ;; package) -- cgit v1.2.3