pax_global_header00006660000000000000000000000064150150211610014502gustar00rootroot0000000000000052 comment=1d09a28c5d4b46c0c5d349874f03939fa57c7661 mrc-1.3.16/000077500000000000000000000000001501502116100123535ustar00rootroot00000000000000mrc-1.3.16/.gitattributes000066400000000000000000000000531501502116100152440ustar00rootroot00000000000000rsrc/*.txt eol=lf rsrc/subdir/*.txt eol=lf mrc-1.3.16/.github/000077500000000000000000000000001501502116100137135ustar00rootroot00000000000000mrc-1.3.16/.github/workflows/000077500000000000000000000000001501502116100157505ustar00rootroot00000000000000mrc-1.3.16/.github/workflows/cmake-multi-platform.yml000066400000000000000000000071011501502116100225240ustar00rootroot00000000000000# This starter workflow is for a CMake project running on multiple platforms. There is a different starter workflow if you just want a single platform. # See: https://github.com/actions/starter-workflows/blob/main/ci/cmake-single-platform.yml name: multi platform test on: push: branches: [ "trunk", "develop" ] pull_request: branches: [ "trunk" ] jobs: build: runs-on: ${{ matrix.os }} strategy: # Set fail-fast to false to ensure that feedback is delivered for all matrix combinations. Consider changing this to true when your workflow is stable. fail-fast: false # Set up a matrix to run the following 3 configurations: # 1. # 2. # 3. # # To add more build types (Release, Debug, RelWithDebInfo, etc.) customize the build_type list. matrix: os: [ubuntu-latest, windows-latest] build_type: [Release] # compilation error with clang/catch2 forced me to comment clang out # c_compiler: [gcc, clang, cl] c_compiler: [gcc, cl] include: - os: windows-latest c_compiler: cl cpp_compiler: cl - os: ubuntu-latest c_compiler: gcc cpp_compiler: g++ # - os: ubuntu-latest # c_compiler: clang # cpp_compiler: clang++ exclude: - os: windows-latest c_compiler: gcc # - os: windows-latest # c_compiler: clang - os: ubuntu-latest c_compiler: cl steps: - uses: actions/checkout@v3 - name: Set reusable strings # Turn repeated input strings (such as the build output directory) into step outputs. These step outputs can be used throughout the workflow file. id: strings shell: bash run: | echo "build-output-dir=${{ github.workspace }}/build" >> "$GITHUB_OUTPUT" - name: Install dependencies Ubuntu if: matrix.os == 'ubuntu-latest' run: > sudo apt-get update && sudo apt-get install catch2 - name: Configure CMake # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type run: > cmake -B ${{ steps.strings.outputs.build-output-dir }} -DCMAKE_CXX_COMPILER=${{ matrix.cpp_compiler }} -DCMAKE_C_COMPILER=${{ matrix.c_compiler }} -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} -S ${{ github.workspace }} - name: Build # Build your program with the given configuration. Note that --config is needed because the default Windows generator is a multi-config generator (Visual Studio generator). run: cmake --build ${{ steps.strings.outputs.build-output-dir }} --config ${{ matrix.build_type }} - name: Test working-directory: ${{ steps.strings.outputs.build-output-dir }} # Execute tests defined by the CMake configuration. Note that --build-config is needed because the default Windows generator is a multi-config generator (Visual Studio generator). # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail run: ctest --build-config ${{ matrix.build_type }} --rerun-failed --output-on-failure mrc-1.3.16/.gitignore000066400000000000000000000001021501502116100143340ustar00rootroot00000000000000.vscode/ **/build/ src/revision.hpp src/version.hpp **/sdkconfig* mrc-1.3.16/.gitmodules000066400000000000000000000001201501502116100145210ustar00rootroot00000000000000[submodule "libmcfp"] path = libmcfp url = git@github.com:mhekkel/libmcfp.git mrc-1.3.16/CMakeLists.txt000066400000000000000000000100631501502116100151130ustar00rootroot00000000000000# Copyright (c) 2021 Maarten L. Hekkelman # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # 1. Redistributions of source code must retain the above copyright notice, this # list of conditions and the following disclaimer # 2. Redistributions in binary form must reproduce the above copyright notice, # this list of conditions and the following disclaimer in the documentation # and/or other materials provided with the distribution. # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. cmake_minimum_required(VERSION 3.10) list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") # set the project name project(mrc VERSION 1.3.16) include(CTest) include(CPM) set(CXX_EXTENSIONS OFF) set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) if(MSVC) # make msvc standards compliant... add_compile_options(/permissive-) endif() if(WIN32) # Windows is always little endian, right? add_compile_definitions(LITTLE_ENDIAN) # And we certainly don't need those pesky min/max macro's add_compile_definitions(NOMINMAX) # Find out the processor type for the target if(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "AMD64") set(COFF_TYPE "x64") elseif(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "i386") set(COFF_TYPE "x86") elseif(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "ARM64") set(COFF_TYPE "arm64") else() message(FATAL_ERROR "Unsupported or unknown processor type ${CMAKE_SYSTEM_PROCESSOR}") endif() set(COFF_SPEC "--coff=${COFF_TYPE}") message(STATUS "Using COFF spec: ${COFF_SPEC}") endif() set(CPM_USE_LOCAL_PACKAGES ON) CPMAddPackage("https://forge.hekkelman.net/maarten/mcfp.git@1.4.1") include(VersionString) write_version_header(${PROJECT_SOURCE_DIR}/src/) add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/mrc-rsrc.obj COMMAND $ -o ${CMAKE_BINARY_DIR}/mrc-rsrc.obj ${PROJECT_SOURCE_DIR}/src/mrsrc.h ${COFF_SPEC} DEPENDS ${PROJECT_SOURCE_DIR}/src/mrsrc.h mrc-bootstrap ) add_executable(mrc ${PROJECT_SOURCE_DIR}/src/mrc.cpp ${CMAKE_BINARY_DIR}/mrc-rsrc.obj) add_executable(mrc-bootstrap ${PROJECT_SOURCE_DIR}/src/mrc.cpp ${PROJECT_SOURCE_DIR}/src/dummy.cpp) target_link_libraries(mrc mcfp::mcfp) target_link_libraries(mrc-bootstrap mcfp::mcfp) if(BUILD_TESTING) CPMAddPackage("gh:catchorg/Catch2@3.5.0") file(GLOB UNIT_TEST_RSRC LIST_DIRECTORIES true ${PROJECT_SOURCE_DIR}/rsrc/*) add_custom_command(OUTPUT mrc-unit-test-rsrc.obj COMMAND $ -o mrc-unit-test-rsrc.obj ${UNIT_TEST_RSRC} ${COFF_SPEC} DEPENDS ${UNIT_TEST_RSRC} mrc-bootstrap ) add_executable(mrc-unit-test ${PROJECT_SOURCE_DIR}/src/mrc-unit-test.cpp ${CMAKE_BINARY_DIR}/mrc-unit-test-rsrc.obj) if(TARGET Catch2::Catch2WithMain) target_link_libraries(mrc-unit-test Catch2::Catch2WithMain) add_test(NAME unit-test COMMAND $ WORKING_DIRECTORY .) else() message(WARNING "No Catch2::Catch2WithMain target, something is wrong") endif() endif() install(TARGETS mrc) if(WIN32) install(FILES cmake/mrc-config.cmake DESTINATION cmake) install(FILES doc/mrc-manual.pdf DESTINATION doc) else() install(FILES cmake/mrc-config.cmake DESTINATION share/mrc/cmake) install(FILES doc/mrc.1 DESTINATION share/man/man1) endif() install(DIRECTORY examples DESTINATION share/doc/mrc PATTERN build EXCLUDE) # add_subdirectory(examples/simple) mrc-1.3.16/LICENSE000066400000000000000000000024461501502116100133660ustar00rootroot00000000000000BSD-2-Clause Copyright (c) 2020 Maarten L. Hekkelman All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. mrc-1.3.16/README.md000066400000000000000000000061521501502116100136360ustar00rootroot00000000000000[![github CI](https://github.com/mhekkel/mrc/actions/workflows/cmake-multi-platform.yml/badge.svg)](https://github.com/mhekkel/mrc/actions) Maartens Resource Compiler ========================== Abstract -------- A long, long time ago there was an operating system that did things differently. One of the cool features of this OS was that applications were self contained. A single file was everything you needed to copy in order to install a new application. No need to use installers or install scripts that put dozens of files at the most obscure locations on your disk. One of the technical features to make this possible was called resources: a kind of database in one of the two forks of a file, the other fork being the data fork. Resources were mildly popular, other OS'es copied the concept in one way or another. The company that invented them abandoned the whole concept though, they thought they had something better.... Anyway, I still like being able to provide a single executable to the users of my software. And given the usefulness of resources I decided to create a compiler generating object files in the ELF executable format. Since using resource forks is not an option I decided to store the data in the static data section of an executable. The data can then be accessed through global variables. New in version 1.3.4 is support for writing COFF files, so you can now use this type of resources in Windows as well. Synopsis -------- First, create the mrsrc.h file that contains C++ classes to access the resources. This file can be generated by executing: ```console mrc --header -o mrsrc.h ``` Then include this file and use it: ```cpp #include "mrsrc.h" int main() { mrsrc::rsrc hello("texts/greeting"); if (hello) { std::string s(hello.data(), hello.size()); std::cout << s << '\n'; } return 0; } ``` To create a resource file: ```console echo "Hello, world!" > greeting mrc -o my-rsrc.o --root texts greeting c++ -o my-app foo.cpp my-rsrc.o ``` CMake integration ----------------- mrc comes with a mrc-config.cmake file installed at a suitable location. This means you can include mrc using a `find_package` call. The previous example might have a _CMakeLists.txt_ file containing the following: ```cmake project(hello VERSION 1.0.0 LANGUAGES CXX) # Include the mrc package file find_package(mrc) # The MRC_FOUND variable is set if MRC was found if(NOT MRC_FOUND) message(FATAL_ERROR "mrc not found") endif() # The executable to create add_executable(mrc-user ${CMAKE_CURRENT_SOURCE_DIR}/src/mrc-user.cpp) # Write out the mrc header file and add the directory to the include paths mrc_write_header(${CMAKE_CURRENT_BINARY_DIR}/mrsrc.hpp) target_include_directories(mrc-user ${CMAKE_CURRENT_BINARY_DIR}) # Add the file hello.txt in the directory rsrc as resource 'hello.txt' mrc_target_resources(mrc-user ${CMAKE_CURRENT_SOURCE_DIR}/rsrc/hello.txt) ``` Building mrc ------------ To build mrc, you should use [cmake](https://cmake.org): ```console git clone https://github.com/mhekkel/mrc.git cd mrc cmake -S . -B build cmake --build build ctest --test-dir build cmake --install build ``` mrc-1.3.16/changelog000066400000000000000000000044561501502116100142360ustar00rootroot00000000000000Version 1.3.16 - Added support for GNU/Hurd (thanks to Pino Toscano) Version 1.3.15 - Yet another fix for the cmake config file Version 1.3.14 - Fixed the cmake code to generate dep files Version 1.3.13 - Readlink does not write a null character, so we have to add it ourselves. Version 1.3.12 - Only use FetchContent when required Version 1.3.11 - Added option --elf-template that can use an object file or executable in ELF format as template to find the correct --elf-* flags to use. This option is used automatically by mrc when you use it via the cmake plugin. - Remove libmcfp as submodule - Moved from Boost.Test to Catch2 for unit testing - Using FetchContent for dependencies - Dropped FindFilesystem, assuming std::filesystem is standard by now. Version 1.3.10 - Check cmake version before trying to write dependency file commands - New version string code Version 1.3.9 - Updated cmake config file: added option to specify coff type in WIN32 manually. - Fix 32 bit version for Windows Version 1.3.8 - Avoid potential crash on uninitialised resources Version 1.3.7 - Added -d/--depends option to write out a depends file - Updated the cmake config to use aforementioned depends file Version 1.3.6 - Properly report files that cannot be read as input - Removed dependency on boost::program_options Version 1.3.5 - Properly set the badbit in case a resources is not found while constructing an istream. Avoids a crash. Version 1.3.4 - Fix the Windows version again. Version 1.3.3 - Add the ABI flag to specify ABI OS version in ELF headers Version 1.3.2 - Fix install rules in CMakeLists.txt - Added example code Version 1.3.1 - Fix writing header to specified output instead of stdout - Added a cmake config file, for easy configuration Version 1.3.0 - Use cmake as new build system instead of autoconf - Write COFF files (for MS Windows) Version 1.2.3 - Should now work on all architectures. - replaced cpu and eabi flags with more generic --elf-xxx options. Version 1.2.2 - Fixed error caused by switching to std::filesystem usage. Version 1.2.0 - Added istream class Version 1.2.0 - Added streambuf class - Added unit tests Version 1.1.0 - New interface file, now no longer throws exception when a resource is not found. So you should check the validity using e.g. operator bool. Version 1.0.0 mrc-1.3.16/cmake/000077500000000000000000000000001501502116100134335ustar00rootroot00000000000000mrc-1.3.16/cmake/CPM.cmake000066400000000000000000001225111501502116100150560ustar00rootroot00000000000000# CPM.cmake - CMake's missing package manager # =========================================== # See https://github.com/cpm-cmake/CPM.cmake for usage and update instructions. # # MIT License # ----------- #[[ Copyright (c) 2019-2023 Lars Melchior and contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ]] cmake_minimum_required(VERSION 3.14 FATAL_ERROR) # Initialize logging prefix if(NOT CPM_INDENT) set(CPM_INDENT "CPM:" CACHE INTERNAL "" ) endif() if(NOT COMMAND cpm_message) function(cpm_message) message(${ARGV}) endfunction() endif() if(DEFINED EXTRACTED_CPM_VERSION) set(CURRENT_CPM_VERSION "${EXTRACTED_CPM_VERSION}${CPM_DEVELOPMENT}") else() set(CURRENT_CPM_VERSION 0.40.8) endif() get_filename_component(CPM_CURRENT_DIRECTORY "${CMAKE_CURRENT_LIST_DIR}" REALPATH) if(CPM_DIRECTORY) if(NOT CPM_DIRECTORY STREQUAL CPM_CURRENT_DIRECTORY) if(CPM_VERSION VERSION_LESS CURRENT_CPM_VERSION) message( AUTHOR_WARNING "${CPM_INDENT} \ A dependency is using a more recent CPM version (${CURRENT_CPM_VERSION}) than the current project (${CPM_VERSION}). \ It is recommended to upgrade CPM to the most recent version. \ See https://github.com/cpm-cmake/CPM.cmake for more information." ) endif() if(${CMAKE_VERSION} VERSION_LESS "3.17.0") include(FetchContent) endif() return() endif() get_property( CPM_INITIALIZED GLOBAL "" PROPERTY CPM_INITIALIZED SET ) if(CPM_INITIALIZED) return() endif() endif() if(CURRENT_CPM_VERSION MATCHES "development-version") message( WARNING "${CPM_INDENT} Your project is using an unstable development version of CPM.cmake. \ Please update to a recent release if possible. \ See https://github.com/cpm-cmake/CPM.cmake for details." ) endif() set_property(GLOBAL PROPERTY CPM_INITIALIZED true) macro(cpm_set_policies) # the policy allows us to change options without caching cmake_policy(SET CMP0077 NEW) set(CMAKE_POLICY_DEFAULT_CMP0077 NEW) # the policy allows us to change set(CACHE) without caching if(POLICY CMP0126) cmake_policy(SET CMP0126 NEW) set(CMAKE_POLICY_DEFAULT_CMP0126 NEW) endif() # The policy uses the download time for timestamp, instead of the timestamp in the archive. This # allows for proper rebuilds when a projects url changes if(POLICY CMP0135) cmake_policy(SET CMP0135 NEW) set(CMAKE_POLICY_DEFAULT_CMP0135 NEW) endif() # treat relative git repository paths as being relative to the parent project's remote if(POLICY CMP0150) cmake_policy(SET CMP0150 NEW) set(CMAKE_POLICY_DEFAULT_CMP0150 NEW) endif() endmacro() cpm_set_policies() option(CPM_USE_LOCAL_PACKAGES "Always try to use `find_package` to get dependencies" $ENV{CPM_USE_LOCAL_PACKAGES} ) option(CPM_LOCAL_PACKAGES_ONLY "Only use `find_package` to get dependencies" $ENV{CPM_LOCAL_PACKAGES_ONLY} ) option(CPM_DOWNLOAD_ALL "Always download dependencies from source" $ENV{CPM_DOWNLOAD_ALL}) option(CPM_DONT_UPDATE_MODULE_PATH "Don't update the module path to allow using find_package" $ENV{CPM_DONT_UPDATE_MODULE_PATH} ) option(CPM_DONT_CREATE_PACKAGE_LOCK "Don't create a package lock file in the binary path" $ENV{CPM_DONT_CREATE_PACKAGE_LOCK} ) option(CPM_INCLUDE_ALL_IN_PACKAGE_LOCK "Add all packages added through CPM.cmake to the package lock" $ENV{CPM_INCLUDE_ALL_IN_PACKAGE_LOCK} ) option(CPM_USE_NAMED_CACHE_DIRECTORIES "Use additional directory of package name in cache on the most nested level." $ENV{CPM_USE_NAMED_CACHE_DIRECTORIES} ) set(CPM_VERSION ${CURRENT_CPM_VERSION} CACHE INTERNAL "" ) set(CPM_DIRECTORY ${CPM_CURRENT_DIRECTORY} CACHE INTERNAL "" ) set(CPM_FILE ${CMAKE_CURRENT_LIST_FILE} CACHE INTERNAL "" ) set(CPM_PACKAGES "" CACHE INTERNAL "" ) set(CPM_DRY_RUN OFF CACHE INTERNAL "Don't download or configure dependencies (for testing)" ) if(DEFINED ENV{CPM_SOURCE_CACHE}) set(CPM_SOURCE_CACHE_DEFAULT $ENV{CPM_SOURCE_CACHE}) else() set(CPM_SOURCE_CACHE_DEFAULT OFF) endif() set(CPM_SOURCE_CACHE ${CPM_SOURCE_CACHE_DEFAULT} CACHE PATH "Directory to download CPM dependencies" ) if(NOT CPM_DONT_UPDATE_MODULE_PATH AND NOT DEFINED CMAKE_FIND_PACKAGE_REDIRECTS_DIR) set(CPM_MODULE_PATH "${CMAKE_BINARY_DIR}/CPM_modules" CACHE INTERNAL "" ) # remove old modules file(REMOVE_RECURSE ${CPM_MODULE_PATH}) file(MAKE_DIRECTORY ${CPM_MODULE_PATH}) # locally added CPM modules should override global packages set(CMAKE_MODULE_PATH "${CPM_MODULE_PATH};${CMAKE_MODULE_PATH}") endif() if(NOT CPM_DONT_CREATE_PACKAGE_LOCK) set(CPM_PACKAGE_LOCK_FILE "${CMAKE_BINARY_DIR}/cpm-package-lock.cmake" CACHE INTERNAL "" ) file(WRITE ${CPM_PACKAGE_LOCK_FILE} "# CPM Package Lock\n# This file should be committed to version control\n\n" ) endif() include(FetchContent) # Try to infer package name from git repository uri (path or url) function(cpm_package_name_from_git_uri URI RESULT) if("${URI}" MATCHES "([^/:]+)/?.git/?$") set(${RESULT} ${CMAKE_MATCH_1} PARENT_SCOPE ) else() unset(${RESULT} PARENT_SCOPE) endif() endfunction() # Try to infer package name and version from a url function(cpm_package_name_and_ver_from_url url outName outVer) if(url MATCHES "[/\\?]([a-zA-Z0-9_\\.-]+)\\.(tar|tar\\.gz|tar\\.bz2|zip|ZIP)(\\?|/|$)") # We matched an archive set(filename "${CMAKE_MATCH_1}") if(filename MATCHES "([a-zA-Z0-9_\\.-]+)[_-]v?(([0-9]+\\.)*[0-9]+[a-zA-Z0-9]*)") # We matched - (ie foo-1.2.3) set(${outName} "${CMAKE_MATCH_1}" PARENT_SCOPE ) set(${outVer} "${CMAKE_MATCH_2}" PARENT_SCOPE ) elseif(filename MATCHES "(([0-9]+\\.)+[0-9]+[a-zA-Z0-9]*)") # We couldn't find a name, but we found a version # # In many cases (which we don't handle here) the url would look something like # `irrelevant/ACTUAL_PACKAGE_NAME/irrelevant/1.2.3.zip`. In such a case we can't possibly # distinguish the package name from the irrelevant bits. Moreover if we try to match the # package name from the filename, we'd get bogus at best. unset(${outName} PARENT_SCOPE) set(${outVer} "${CMAKE_MATCH_1}" PARENT_SCOPE ) else() # Boldly assume that the file name is the package name. # # Yes, something like `irrelevant/ACTUAL_NAME/irrelevant/download.zip` will ruin our day, but # such cases should be quite rare. No popular service does this... we think. set(${outName} "${filename}" PARENT_SCOPE ) unset(${outVer} PARENT_SCOPE) endif() else() # No ideas yet what to do with non-archives unset(${outName} PARENT_SCOPE) unset(${outVer} PARENT_SCOPE) endif() endfunction() function(cpm_find_package NAME VERSION) string(REPLACE " " ";" EXTRA_ARGS "${ARGN}") find_package(${NAME} ${VERSION} ${EXTRA_ARGS} QUIET) if(${CPM_ARGS_NAME}_FOUND) if(DEFINED ${CPM_ARGS_NAME}_VERSION) set(VERSION ${${CPM_ARGS_NAME}_VERSION}) endif() cpm_message(STATUS "${CPM_INDENT} Using local package ${CPM_ARGS_NAME}@${VERSION}") CPMRegisterPackage(${CPM_ARGS_NAME} "${VERSION}") set(CPM_PACKAGE_FOUND YES PARENT_SCOPE ) else() set(CPM_PACKAGE_FOUND NO PARENT_SCOPE ) endif() endfunction() # Create a custom FindXXX.cmake module for a CPM package This prevents `find_package(NAME)` from # finding the system library function(cpm_create_module_file Name) if(NOT CPM_DONT_UPDATE_MODULE_PATH) if(DEFINED CMAKE_FIND_PACKAGE_REDIRECTS_DIR) # Redirect find_package calls to the CPM package. This is what FetchContent does when you set # OVERRIDE_FIND_PACKAGE. The CMAKE_FIND_PACKAGE_REDIRECTS_DIR works for find_package in CONFIG # mode, unlike the Find${Name}.cmake fallback. CMAKE_FIND_PACKAGE_REDIRECTS_DIR is not defined # in script mode, or in CMake < 3.24. # https://cmake.org/cmake/help/latest/module/FetchContent.html#fetchcontent-find-package-integration-examples string(TOLOWER ${Name} NameLower) file(WRITE ${CMAKE_FIND_PACKAGE_REDIRECTS_DIR}/${NameLower}-config.cmake "include(\"\${CMAKE_CURRENT_LIST_DIR}/${NameLower}-extra.cmake\" OPTIONAL)\n" "include(\"\${CMAKE_CURRENT_LIST_DIR}/${Name}Extra.cmake\" OPTIONAL)\n" ) file(WRITE ${CMAKE_FIND_PACKAGE_REDIRECTS_DIR}/${NameLower}-config-version.cmake "set(PACKAGE_VERSION_COMPATIBLE TRUE)\n" "set(PACKAGE_VERSION_EXACT TRUE)\n" ) else() file(WRITE ${CPM_MODULE_PATH}/Find${Name}.cmake "include(\"${CPM_FILE}\")\n${ARGN}\nset(${Name}_FOUND TRUE)" ) endif() endif() endfunction() # Find a package locally or fallback to CPMAddPackage function(CPMFindPackage) set(oneValueArgs NAME VERSION GIT_TAG FIND_PACKAGE_ARGUMENTS) cmake_parse_arguments(CPM_ARGS "" "${oneValueArgs}" "" ${ARGN}) if(NOT DEFINED CPM_ARGS_VERSION) if(DEFINED CPM_ARGS_GIT_TAG) cpm_get_version_from_git_tag("${CPM_ARGS_GIT_TAG}" CPM_ARGS_VERSION) endif() endif() set(downloadPackage ${CPM_DOWNLOAD_ALL}) if(DEFINED CPM_DOWNLOAD_${CPM_ARGS_NAME}) set(downloadPackage ${CPM_DOWNLOAD_${CPM_ARGS_NAME}}) elseif(DEFINED ENV{CPM_DOWNLOAD_${CPM_ARGS_NAME}}) set(downloadPackage $ENV{CPM_DOWNLOAD_${CPM_ARGS_NAME}}) endif() if(downloadPackage) CPMAddPackage(${ARGN}) cpm_export_variables(${CPM_ARGS_NAME}) return() endif() cpm_find_package(${CPM_ARGS_NAME} "${CPM_ARGS_VERSION}" ${CPM_ARGS_FIND_PACKAGE_ARGUMENTS}) if(NOT CPM_PACKAGE_FOUND) CPMAddPackage(${ARGN}) cpm_export_variables(${CPM_ARGS_NAME}) endif() endfunction() # checks if a package has been added before function(cpm_check_if_package_already_added CPM_ARGS_NAME CPM_ARGS_VERSION) if("${CPM_ARGS_NAME}" IN_LIST CPM_PACKAGES) CPMGetPackageVersion(${CPM_ARGS_NAME} CPM_PACKAGE_VERSION) if("${CPM_PACKAGE_VERSION}" VERSION_LESS "${CPM_ARGS_VERSION}") message( WARNING "${CPM_INDENT} Requires a newer version of ${CPM_ARGS_NAME} (${CPM_ARGS_VERSION}) than currently included (${CPM_PACKAGE_VERSION})." ) endif() cpm_get_fetch_properties(${CPM_ARGS_NAME}) set(${CPM_ARGS_NAME}_ADDED NO) set(CPM_PACKAGE_ALREADY_ADDED YES PARENT_SCOPE ) cpm_export_variables(${CPM_ARGS_NAME}) else() set(CPM_PACKAGE_ALREADY_ADDED NO PARENT_SCOPE ) endif() endfunction() # Parse the argument of CPMAddPackage in case a single one was provided and convert it to a list of # arguments which can then be parsed idiomatically. For example gh:foo/bar@1.2.3 will be converted # to: GITHUB_REPOSITORY;foo/bar;VERSION;1.2.3 function(cpm_parse_add_package_single_arg arg outArgs) # Look for a scheme if("${arg}" MATCHES "^([a-zA-Z]+):(.+)$") string(TOLOWER "${CMAKE_MATCH_1}" scheme) set(uri "${CMAKE_MATCH_2}") # Check for CPM-specific schemes if(scheme STREQUAL "gh") set(out "GITHUB_REPOSITORY;${uri}") set(packageType "git") elseif(scheme STREQUAL "gl") set(out "GITLAB_REPOSITORY;${uri}") set(packageType "git") elseif(scheme STREQUAL "bb") set(out "BITBUCKET_REPOSITORY;${uri}") set(packageType "git") # A CPM-specific scheme was not found. Looks like this is a generic URL so try to determine # type elseif(arg MATCHES ".git/?(@|#|$)") set(out "GIT_REPOSITORY;${arg}") set(packageType "git") else() # Fall back to a URL set(out "URL;${arg}") set(packageType "archive") # We could also check for SVN since FetchContent supports it, but SVN is so rare these days. # We just won't bother with the additional complexity it will induce in this function. SVN is # done by multi-arg endif() else() if(arg MATCHES ".git/?(@|#|$)") set(out "GIT_REPOSITORY;${arg}") set(packageType "git") else() # Give up message(FATAL_ERROR "${CPM_INDENT} Can't determine package type of '${arg}'") endif() endif() # For all packages we interpret @... as version. Only replace the last occurrence. Thus URIs # containing '@' can be used string(REGEX REPLACE "@([^@]+)$" ";VERSION;\\1" out "${out}") # Parse the rest according to package type if(packageType STREQUAL "git") # For git repos we interpret #... as a tag or branch or commit hash string(REGEX REPLACE "#([^#]+)$" ";GIT_TAG;\\1" out "${out}") elseif(packageType STREQUAL "archive") # For archives we interpret #... as a URL hash. string(REGEX REPLACE "#([^#]+)$" ";URL_HASH;\\1" out "${out}") # We don't try to parse the version if it's not provided explicitly. cpm_get_version_from_url # should do this at a later point else() # We should never get here. This is an assertion and hitting it means there's a problem with the # code above. A packageType was set, but not handled by this if-else. message(FATAL_ERROR "${CPM_INDENT} Unsupported package type '${packageType}' of '${arg}'") endif() set(${outArgs} ${out} PARENT_SCOPE ) endfunction() # Check that the working directory for a git repo is clean function(cpm_check_git_working_dir_is_clean repoPath gitTag isClean) find_package(Git REQUIRED) if(NOT GIT_EXECUTABLE) # No git executable, assume directory is clean set(${isClean} TRUE PARENT_SCOPE ) return() endif() # check for uncommitted changes execute_process( COMMAND ${GIT_EXECUTABLE} status --porcelain RESULT_VARIABLE resultGitStatus OUTPUT_VARIABLE repoStatus OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_QUIET WORKING_DIRECTORY ${repoPath} ) if(resultGitStatus) # not supposed to happen, assume clean anyway message(WARNING "${CPM_INDENT} Calling git status on folder ${repoPath} failed") set(${isClean} TRUE PARENT_SCOPE ) return() endif() if(NOT "${repoStatus}" STREQUAL "") set(${isClean} FALSE PARENT_SCOPE ) return() endif() # check for committed changes execute_process( COMMAND ${GIT_EXECUTABLE} diff -s --exit-code ${gitTag} RESULT_VARIABLE resultGitDiff OUTPUT_STRIP_TRAILING_WHITESPACE OUTPUT_QUIET WORKING_DIRECTORY ${repoPath} ) if(${resultGitDiff} EQUAL 0) set(${isClean} TRUE PARENT_SCOPE ) else() set(${isClean} FALSE PARENT_SCOPE ) endif() endfunction() # Add PATCH_COMMAND to CPM_ARGS_UNPARSED_ARGUMENTS. This method consumes a list of files in ARGN # then generates a `PATCH_COMMAND` appropriate for `ExternalProject_Add()`. This command is appended # to the parent scope's `CPM_ARGS_UNPARSED_ARGUMENTS`. function(cpm_add_patches) # Return if no patch files are supplied. if(NOT ARGN) return() endif() # Find the patch program. find_program(PATCH_EXECUTABLE patch) if(CMAKE_HOST_WIN32 AND NOT PATCH_EXECUTABLE) # The Windows git executable is distributed with patch.exe. Find the path to the executable, if # it exists, then search `../usr/bin` and `../../usr/bin` for patch.exe. find_package(Git QUIET) if(GIT_EXECUTABLE) get_filename_component(extra_search_path ${GIT_EXECUTABLE} DIRECTORY) get_filename_component(extra_search_path_1up ${extra_search_path} DIRECTORY) get_filename_component(extra_search_path_2up ${extra_search_path_1up} DIRECTORY) find_program( PATCH_EXECUTABLE patch HINTS "${extra_search_path_1up}/usr/bin" "${extra_search_path_2up}/usr/bin" ) endif() endif() if(NOT PATCH_EXECUTABLE) message(FATAL_ERROR "Couldn't find `patch` executable to use with PATCHES keyword.") endif() # Create a temporary set(temp_list ${CPM_ARGS_UNPARSED_ARGUMENTS}) # Ensure each file exists (or error out) and add it to the list. set(first_item True) foreach(PATCH_FILE ${ARGN}) # Make sure the patch file exists, if we can't find it, try again in the current directory. if(NOT EXISTS "${PATCH_FILE}") if(NOT EXISTS "${CMAKE_CURRENT_LIST_DIR}/${PATCH_FILE}") message(FATAL_ERROR "Couldn't find patch file: '${PATCH_FILE}'") endif() set(PATCH_FILE "${CMAKE_CURRENT_LIST_DIR}/${PATCH_FILE}") endif() # Convert to absolute path for use with patch file command. get_filename_component(PATCH_FILE "${PATCH_FILE}" ABSOLUTE) # The first patch entry must be preceded by "PATCH_COMMAND" while the following items are # preceded by "&&". if(first_item) set(first_item False) list(APPEND temp_list "PATCH_COMMAND") else() list(APPEND temp_list "&&") endif() # Add the patch command to the list list(APPEND temp_list "${PATCH_EXECUTABLE}" "-p1" "<" "${PATCH_FILE}") endforeach() # Move temp out into parent scope. set(CPM_ARGS_UNPARSED_ARGUMENTS ${temp_list} PARENT_SCOPE ) endfunction() # method to overwrite internal FetchContent properties, to allow using CPM.cmake to overload # FetchContent calls. As these are internal cmake properties, this method should be used carefully # and may need modification in future CMake versions. Source: # https://github.com/Kitware/CMake/blob/dc3d0b5a0a7d26d43d6cfeb511e224533b5d188f/Modules/FetchContent.cmake#L1152 function(cpm_override_fetchcontent contentName) cmake_parse_arguments(PARSE_ARGV 1 arg "" "SOURCE_DIR;BINARY_DIR" "") if(NOT "${arg_UNPARSED_ARGUMENTS}" STREQUAL "") message(FATAL_ERROR "${CPM_INDENT} Unsupported arguments: ${arg_UNPARSED_ARGUMENTS}") endif() string(TOLOWER ${contentName} contentNameLower) set(prefix "_FetchContent_${contentNameLower}") set(propertyName "${prefix}_sourceDir") define_property( GLOBAL PROPERTY ${propertyName} BRIEF_DOCS "Internal implementation detail of FetchContent_Populate()" FULL_DOCS "Details used by FetchContent_Populate() for ${contentName}" ) set_property(GLOBAL PROPERTY ${propertyName} "${arg_SOURCE_DIR}") set(propertyName "${prefix}_binaryDir") define_property( GLOBAL PROPERTY ${propertyName} BRIEF_DOCS "Internal implementation detail of FetchContent_Populate()" FULL_DOCS "Details used by FetchContent_Populate() for ${contentName}" ) set_property(GLOBAL PROPERTY ${propertyName} "${arg_BINARY_DIR}") set(propertyName "${prefix}_populated") define_property( GLOBAL PROPERTY ${propertyName} BRIEF_DOCS "Internal implementation detail of FetchContent_Populate()" FULL_DOCS "Details used by FetchContent_Populate() for ${contentName}" ) set_property(GLOBAL PROPERTY ${propertyName} TRUE) endfunction() # Download and add a package from source function(CPMAddPackage) cpm_set_policies() list(LENGTH ARGN argnLength) if(argnLength EQUAL 1) cpm_parse_add_package_single_arg("${ARGN}" ARGN) # The shorthand syntax implies EXCLUDE_FROM_ALL and SYSTEM set(ARGN "${ARGN};EXCLUDE_FROM_ALL;YES;SYSTEM;YES;") endif() set(oneValueArgs NAME FORCE VERSION GIT_TAG DOWNLOAD_ONLY GITHUB_REPOSITORY GITLAB_REPOSITORY BITBUCKET_REPOSITORY GIT_REPOSITORY SOURCE_DIR FIND_PACKAGE_ARGUMENTS NO_CACHE SYSTEM GIT_SHALLOW EXCLUDE_FROM_ALL SOURCE_SUBDIR CUSTOM_CACHE_KEY ) set(multiValueArgs URL OPTIONS DOWNLOAD_COMMAND PATCHES) cmake_parse_arguments(CPM_ARGS "" "${oneValueArgs}" "${multiValueArgs}" "${ARGN}") # Set default values for arguments if(NOT DEFINED CPM_ARGS_VERSION) if(DEFINED CPM_ARGS_GIT_TAG) cpm_get_version_from_git_tag("${CPM_ARGS_GIT_TAG}" CPM_ARGS_VERSION) endif() endif() if(CPM_ARGS_DOWNLOAD_ONLY) set(DOWNLOAD_ONLY ${CPM_ARGS_DOWNLOAD_ONLY}) else() set(DOWNLOAD_ONLY NO) endif() if(DEFINED CPM_ARGS_GITHUB_REPOSITORY) set(CPM_ARGS_GIT_REPOSITORY "https://github.com/${CPM_ARGS_GITHUB_REPOSITORY}.git") elseif(DEFINED CPM_ARGS_GITLAB_REPOSITORY) set(CPM_ARGS_GIT_REPOSITORY "https://gitlab.com/${CPM_ARGS_GITLAB_REPOSITORY}.git") elseif(DEFINED CPM_ARGS_BITBUCKET_REPOSITORY) set(CPM_ARGS_GIT_REPOSITORY "https://bitbucket.org/${CPM_ARGS_BITBUCKET_REPOSITORY}.git") endif() if(DEFINED CPM_ARGS_GIT_REPOSITORY) list(APPEND CPM_ARGS_UNPARSED_ARGUMENTS GIT_REPOSITORY ${CPM_ARGS_GIT_REPOSITORY}) if(NOT DEFINED CPM_ARGS_GIT_TAG) set(CPM_ARGS_GIT_TAG v${CPM_ARGS_VERSION}) endif() # If a name wasn't provided, try to infer it from the git repo if(NOT DEFINED CPM_ARGS_NAME) cpm_package_name_from_git_uri(${CPM_ARGS_GIT_REPOSITORY} CPM_ARGS_NAME) endif() endif() set(CPM_SKIP_FETCH FALSE) if(DEFINED CPM_ARGS_GIT_TAG) list(APPEND CPM_ARGS_UNPARSED_ARGUMENTS GIT_TAG ${CPM_ARGS_GIT_TAG}) # If GIT_SHALLOW is explicitly specified, honor the value. if(DEFINED CPM_ARGS_GIT_SHALLOW) list(APPEND CPM_ARGS_UNPARSED_ARGUMENTS GIT_SHALLOW ${CPM_ARGS_GIT_SHALLOW}) endif() endif() if(DEFINED CPM_ARGS_URL) # If a name or version aren't provided, try to infer them from the URL list(GET CPM_ARGS_URL 0 firstUrl) cpm_package_name_and_ver_from_url(${firstUrl} nameFromUrl verFromUrl) # If we fail to obtain name and version from the first URL, we could try other URLs if any. # However multiple URLs are expected to be quite rare, so for now we won't bother. # If the caller provided their own name and version, they trump the inferred ones. if(NOT DEFINED CPM_ARGS_NAME) set(CPM_ARGS_NAME ${nameFromUrl}) endif() if(NOT DEFINED CPM_ARGS_VERSION) set(CPM_ARGS_VERSION ${verFromUrl}) endif() list(APPEND CPM_ARGS_UNPARSED_ARGUMENTS URL "${CPM_ARGS_URL}") endif() # Check for required arguments if(NOT DEFINED CPM_ARGS_NAME) message( FATAL_ERROR "${CPM_INDENT} 'NAME' was not provided and couldn't be automatically inferred for package added with arguments: '${ARGN}'" ) endif() # Check if package has been added before cpm_check_if_package_already_added(${CPM_ARGS_NAME} "${CPM_ARGS_VERSION}") if(CPM_PACKAGE_ALREADY_ADDED) cpm_export_variables(${CPM_ARGS_NAME}) return() endif() # Check for manual overrides if(NOT CPM_ARGS_FORCE AND NOT "${CPM_${CPM_ARGS_NAME}_SOURCE}" STREQUAL "") set(PACKAGE_SOURCE ${CPM_${CPM_ARGS_NAME}_SOURCE}) set(CPM_${CPM_ARGS_NAME}_SOURCE "") CPMAddPackage( NAME "${CPM_ARGS_NAME}" SOURCE_DIR "${PACKAGE_SOURCE}" EXCLUDE_FROM_ALL "${CPM_ARGS_EXCLUDE_FROM_ALL}" SYSTEM "${CPM_ARGS_SYSTEM}" PATCHES "${CPM_ARGS_PATCHES}" OPTIONS "${CPM_ARGS_OPTIONS}" SOURCE_SUBDIR "${CPM_ARGS_SOURCE_SUBDIR}" DOWNLOAD_ONLY "${DOWNLOAD_ONLY}" FORCE True ) cpm_export_variables(${CPM_ARGS_NAME}) return() endif() # Check for available declaration if(NOT CPM_ARGS_FORCE AND NOT "${CPM_DECLARATION_${CPM_ARGS_NAME}}" STREQUAL "") set(declaration ${CPM_DECLARATION_${CPM_ARGS_NAME}}) set(CPM_DECLARATION_${CPM_ARGS_NAME} "") CPMAddPackage(${declaration}) cpm_export_variables(${CPM_ARGS_NAME}) # checking again to ensure version and option compatibility cpm_check_if_package_already_added(${CPM_ARGS_NAME} "${CPM_ARGS_VERSION}") return() endif() if(NOT CPM_ARGS_FORCE) if(CPM_USE_LOCAL_PACKAGES OR CPM_LOCAL_PACKAGES_ONLY) cpm_find_package(${CPM_ARGS_NAME} "${CPM_ARGS_VERSION}" ${CPM_ARGS_FIND_PACKAGE_ARGUMENTS}) if(CPM_PACKAGE_FOUND) cpm_export_variables(${CPM_ARGS_NAME}) return() endif() if(CPM_LOCAL_PACKAGES_ONLY) message( SEND_ERROR "${CPM_INDENT} ${CPM_ARGS_NAME} not found via find_package(${CPM_ARGS_NAME} ${CPM_ARGS_VERSION})" ) endif() endif() endif() CPMRegisterPackage("${CPM_ARGS_NAME}" "${CPM_ARGS_VERSION}") if(DEFINED CPM_ARGS_GIT_TAG) set(PACKAGE_INFO "${CPM_ARGS_GIT_TAG}") elseif(DEFINED CPM_ARGS_SOURCE_DIR) set(PACKAGE_INFO "${CPM_ARGS_SOURCE_DIR}") else() set(PACKAGE_INFO "${CPM_ARGS_VERSION}") endif() if(DEFINED FETCHCONTENT_BASE_DIR) # respect user's FETCHCONTENT_BASE_DIR if set set(CPM_FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR}) else() set(CPM_FETCHCONTENT_BASE_DIR ${CMAKE_BINARY_DIR}/_deps) endif() cpm_add_patches(${CPM_ARGS_PATCHES}) if(DEFINED CPM_ARGS_DOWNLOAD_COMMAND) list(APPEND CPM_ARGS_UNPARSED_ARGUMENTS DOWNLOAD_COMMAND ${CPM_ARGS_DOWNLOAD_COMMAND}) elseif(DEFINED CPM_ARGS_SOURCE_DIR) list(APPEND CPM_ARGS_UNPARSED_ARGUMENTS SOURCE_DIR ${CPM_ARGS_SOURCE_DIR}) if(NOT IS_ABSOLUTE ${CPM_ARGS_SOURCE_DIR}) # Expand `CPM_ARGS_SOURCE_DIR` relative path. This is important because EXISTS doesn't work # for relative paths. get_filename_component( source_directory ${CPM_ARGS_SOURCE_DIR} REALPATH BASE_DIR ${CMAKE_CURRENT_BINARY_DIR} ) else() set(source_directory ${CPM_ARGS_SOURCE_DIR}) endif() if(NOT EXISTS ${source_directory}) string(TOLOWER ${CPM_ARGS_NAME} lower_case_name) # remove timestamps so CMake will re-download the dependency file(REMOVE_RECURSE "${CPM_FETCHCONTENT_BASE_DIR}/${lower_case_name}-subbuild") endif() elseif(CPM_SOURCE_CACHE AND NOT CPM_ARGS_NO_CACHE) string(TOLOWER ${CPM_ARGS_NAME} lower_case_name) set(origin_parameters ${CPM_ARGS_UNPARSED_ARGUMENTS}) list(SORT origin_parameters) if(CPM_ARGS_CUSTOM_CACHE_KEY) # Application set a custom unique directory name set(download_directory ${CPM_SOURCE_CACHE}/${lower_case_name}/${CPM_ARGS_CUSTOM_CACHE_KEY}) elseif(CPM_USE_NAMED_CACHE_DIRECTORIES) string(SHA1 origin_hash "${origin_parameters};NEW_CACHE_STRUCTURE_TAG") set(download_directory ${CPM_SOURCE_CACHE}/${lower_case_name}/${origin_hash}/${CPM_ARGS_NAME}) else() string(SHA1 origin_hash "${origin_parameters}") set(download_directory ${CPM_SOURCE_CACHE}/${lower_case_name}/${origin_hash}) endif() # Expand `download_directory` relative path. This is important because EXISTS doesn't work for # relative paths. get_filename_component(download_directory ${download_directory} ABSOLUTE) list(APPEND CPM_ARGS_UNPARSED_ARGUMENTS SOURCE_DIR ${download_directory}) if(CPM_SOURCE_CACHE) file(LOCK ${download_directory}/../cmake.lock) endif() if(EXISTS ${download_directory}) if(CPM_SOURCE_CACHE) file(LOCK ${download_directory}/../cmake.lock RELEASE) endif() cpm_store_fetch_properties( ${CPM_ARGS_NAME} "${download_directory}" "${CPM_FETCHCONTENT_BASE_DIR}/${lower_case_name}-build" ) cpm_get_fetch_properties("${CPM_ARGS_NAME}") if(DEFINED CPM_ARGS_GIT_TAG AND NOT (PATCH_COMMAND IN_LIST CPM_ARGS_UNPARSED_ARGUMENTS)) # warn if cache has been changed since checkout cpm_check_git_working_dir_is_clean(${download_directory} ${CPM_ARGS_GIT_TAG} IS_CLEAN) if(NOT ${IS_CLEAN}) message( WARNING "${CPM_INDENT} Cache for ${CPM_ARGS_NAME} (${download_directory}) is dirty" ) endif() endif() cpm_add_subdirectory( "${CPM_ARGS_NAME}" "${DOWNLOAD_ONLY}" "${${CPM_ARGS_NAME}_SOURCE_DIR}/${CPM_ARGS_SOURCE_SUBDIR}" "${${CPM_ARGS_NAME}_BINARY_DIR}" "${CPM_ARGS_EXCLUDE_FROM_ALL}" "${CPM_ARGS_SYSTEM}" "${CPM_ARGS_OPTIONS}" ) set(PACKAGE_INFO "${PACKAGE_INFO} at ${download_directory}") # As the source dir is already cached/populated, we override the call to FetchContent. set(CPM_SKIP_FETCH TRUE) cpm_override_fetchcontent( "${lower_case_name}" SOURCE_DIR "${${CPM_ARGS_NAME}_SOURCE_DIR}/${CPM_ARGS_SOURCE_SUBDIR}" BINARY_DIR "${${CPM_ARGS_NAME}_BINARY_DIR}" ) else() # Enable shallow clone when GIT_TAG is not a commit hash. Our guess may not be accurate, but # it should guarantee no commit hash get mis-detected. if(NOT DEFINED CPM_ARGS_GIT_SHALLOW) cpm_is_git_tag_commit_hash("${CPM_ARGS_GIT_TAG}" IS_HASH) if(NOT ${IS_HASH}) list(APPEND CPM_ARGS_UNPARSED_ARGUMENTS GIT_SHALLOW TRUE) endif() endif() # remove timestamps so CMake will re-download the dependency file(REMOVE_RECURSE ${CPM_FETCHCONTENT_BASE_DIR}/${lower_case_name}-subbuild) set(PACKAGE_INFO "${PACKAGE_INFO} to ${download_directory}") endif() endif() if(NOT "${DOWNLOAD_ONLY}") cpm_create_module_file(${CPM_ARGS_NAME} "CPMAddPackage(\"${ARGN}\")") endif() if(CPM_PACKAGE_LOCK_ENABLED) if((CPM_ARGS_VERSION AND NOT CPM_ARGS_SOURCE_DIR) OR CPM_INCLUDE_ALL_IN_PACKAGE_LOCK) cpm_add_to_package_lock(${CPM_ARGS_NAME} "${ARGN}") elseif(CPM_ARGS_SOURCE_DIR) cpm_add_comment_to_package_lock(${CPM_ARGS_NAME} "local directory") else() cpm_add_comment_to_package_lock(${CPM_ARGS_NAME} "${ARGN}") endif() endif() cpm_message( STATUS "${CPM_INDENT} Adding package ${CPM_ARGS_NAME}@${CPM_ARGS_VERSION} (${PACKAGE_INFO})" ) if(NOT CPM_SKIP_FETCH) # CMake 3.28 added EXCLUDE, SYSTEM (3.25), and SOURCE_SUBDIR (3.18) to FetchContent_Declare. # Calling FetchContent_MakeAvailable will then internally forward these options to # add_subdirectory. Up until these changes, we had to call FetchContent_Populate and # add_subdirectory separately, which is no longer necessary and has been deprecated as of 3.30. # A Bug in CMake prevents us to use the non-deprecated functions until 3.30.3. set(fetchContentDeclareExtraArgs "") if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.30.3") if(${CPM_ARGS_EXCLUDE_FROM_ALL}) list(APPEND fetchContentDeclareExtraArgs EXCLUDE_FROM_ALL) endif() if(${CPM_ARGS_SYSTEM}) list(APPEND fetchContentDeclareExtraArgs SYSTEM) endif() if(DEFINED CPM_ARGS_SOURCE_SUBDIR) list(APPEND fetchContentDeclareExtraArgs SOURCE_SUBDIR ${CPM_ARGS_SOURCE_SUBDIR}) endif() # For CMake version <3.28 OPTIONS are parsed in cpm_add_subdirectory if(CPM_ARGS_OPTIONS AND NOT DOWNLOAD_ONLY) foreach(OPTION ${CPM_ARGS_OPTIONS}) cpm_parse_option("${OPTION}") set(${OPTION_KEY} "${OPTION_VALUE}") endforeach() endif() endif() cpm_declare_fetch( "${CPM_ARGS_NAME}" ${fetchContentDeclareExtraArgs} "${CPM_ARGS_UNPARSED_ARGUMENTS}" ) cpm_fetch_package("${CPM_ARGS_NAME}" ${DOWNLOAD_ONLY} populated ${CPM_ARGS_UNPARSED_ARGUMENTS}) if(CPM_SOURCE_CACHE AND download_directory) file(LOCK ${download_directory}/../cmake.lock RELEASE) endif() if(${populated} AND ${CMAKE_VERSION} VERSION_LESS "3.30.3") cpm_add_subdirectory( "${CPM_ARGS_NAME}" "${DOWNLOAD_ONLY}" "${${CPM_ARGS_NAME}_SOURCE_DIR}/${CPM_ARGS_SOURCE_SUBDIR}" "${${CPM_ARGS_NAME}_BINARY_DIR}" "${CPM_ARGS_EXCLUDE_FROM_ALL}" "${CPM_ARGS_SYSTEM}" "${CPM_ARGS_OPTIONS}" ) endif() cpm_get_fetch_properties("${CPM_ARGS_NAME}") endif() set(${CPM_ARGS_NAME}_ADDED YES) cpm_export_variables("${CPM_ARGS_NAME}") endfunction() # Fetch a previously declared package macro(CPMGetPackage Name) if(DEFINED "CPM_DECLARATION_${Name}") CPMAddPackage(NAME ${Name}) else() message(SEND_ERROR "${CPM_INDENT} Cannot retrieve package ${Name}: no declaration available") endif() endmacro() # export variables available to the caller to the parent scope expects ${CPM_ARGS_NAME} to be set macro(cpm_export_variables name) set(${name}_SOURCE_DIR "${${name}_SOURCE_DIR}" PARENT_SCOPE ) set(${name}_BINARY_DIR "${${name}_BINARY_DIR}" PARENT_SCOPE ) set(${name}_ADDED "${${name}_ADDED}" PARENT_SCOPE ) set(CPM_LAST_PACKAGE_NAME "${name}" PARENT_SCOPE ) endmacro() # declares a package, so that any call to CPMAddPackage for the package name will use these # arguments instead. Previous declarations will not be overridden. macro(CPMDeclarePackage Name) if(NOT DEFINED "CPM_DECLARATION_${Name}") set("CPM_DECLARATION_${Name}" "${ARGN}") endif() endmacro() function(cpm_add_to_package_lock Name) if(NOT CPM_DONT_CREATE_PACKAGE_LOCK) cpm_prettify_package_arguments(PRETTY_ARGN false ${ARGN}) file(APPEND ${CPM_PACKAGE_LOCK_FILE} "# ${Name}\nCPMDeclarePackage(${Name}\n${PRETTY_ARGN})\n") endif() endfunction() function(cpm_add_comment_to_package_lock Name) if(NOT CPM_DONT_CREATE_PACKAGE_LOCK) cpm_prettify_package_arguments(PRETTY_ARGN true ${ARGN}) file(APPEND ${CPM_PACKAGE_LOCK_FILE} "# ${Name} (unversioned)\n# CPMDeclarePackage(${Name}\n${PRETTY_ARGN}#)\n" ) endif() endfunction() # includes the package lock file if it exists and creates a target `cpm-update-package-lock` to # update it macro(CPMUsePackageLock file) if(NOT CPM_DONT_CREATE_PACKAGE_LOCK) get_filename_component(CPM_ABSOLUTE_PACKAGE_LOCK_PATH ${file} ABSOLUTE) if(EXISTS ${CPM_ABSOLUTE_PACKAGE_LOCK_PATH}) include(${CPM_ABSOLUTE_PACKAGE_LOCK_PATH}) endif() if(NOT TARGET cpm-update-package-lock) add_custom_target( cpm-update-package-lock COMMAND ${CMAKE_COMMAND} -E copy ${CPM_PACKAGE_LOCK_FILE} ${CPM_ABSOLUTE_PACKAGE_LOCK_PATH} ) endif() set(CPM_PACKAGE_LOCK_ENABLED true) endif() endmacro() # registers a package that has been added to CPM function(CPMRegisterPackage PACKAGE VERSION) list(APPEND CPM_PACKAGES ${PACKAGE}) set(CPM_PACKAGES ${CPM_PACKAGES} CACHE INTERNAL "" ) set("CPM_PACKAGE_${PACKAGE}_VERSION" ${VERSION} CACHE INTERNAL "" ) endfunction() # retrieve the current version of the package to ${OUTPUT} function(CPMGetPackageVersion PACKAGE OUTPUT) set(${OUTPUT} "${CPM_PACKAGE_${PACKAGE}_VERSION}" PARENT_SCOPE ) endfunction() # declares a package in FetchContent_Declare function(cpm_declare_fetch PACKAGE) if(${CPM_DRY_RUN}) cpm_message(STATUS "${CPM_INDENT} Package not declared (dry run)") return() endif() FetchContent_Declare(${PACKAGE} ${ARGN}) endfunction() # returns properties for a package previously defined by cpm_declare_fetch function(cpm_get_fetch_properties PACKAGE) if(${CPM_DRY_RUN}) return() endif() set(${PACKAGE}_SOURCE_DIR "${CPM_PACKAGE_${PACKAGE}_SOURCE_DIR}" PARENT_SCOPE ) set(${PACKAGE}_BINARY_DIR "${CPM_PACKAGE_${PACKAGE}_BINARY_DIR}" PARENT_SCOPE ) endfunction() function(cpm_store_fetch_properties PACKAGE source_dir binary_dir) if(${CPM_DRY_RUN}) return() endif() set(CPM_PACKAGE_${PACKAGE}_SOURCE_DIR "${source_dir}" CACHE INTERNAL "" ) set(CPM_PACKAGE_${PACKAGE}_BINARY_DIR "${binary_dir}" CACHE INTERNAL "" ) endfunction() # adds a package as a subdirectory if viable, according to provided options function( cpm_add_subdirectory PACKAGE DOWNLOAD_ONLY SOURCE_DIR BINARY_DIR EXCLUDE SYSTEM OPTIONS ) if(NOT DOWNLOAD_ONLY AND EXISTS ${SOURCE_DIR}/CMakeLists.txt) set(addSubdirectoryExtraArgs "") if(EXCLUDE) list(APPEND addSubdirectoryExtraArgs EXCLUDE_FROM_ALL) endif() if("${SYSTEM}" AND "${CMAKE_VERSION}" VERSION_GREATER_EQUAL "3.25") # https://cmake.org/cmake/help/latest/prop_dir/SYSTEM.html#prop_dir:SYSTEM list(APPEND addSubdirectoryExtraArgs SYSTEM) endif() if(OPTIONS) foreach(OPTION ${OPTIONS}) cpm_parse_option("${OPTION}") set(${OPTION_KEY} "${OPTION_VALUE}") endforeach() endif() set(CPM_OLD_INDENT "${CPM_INDENT}") set(CPM_INDENT "${CPM_INDENT} ${PACKAGE}:") add_subdirectory(${SOURCE_DIR} ${BINARY_DIR} ${addSubdirectoryExtraArgs}) set(CPM_INDENT "${CPM_OLD_INDENT}") endif() endfunction() # downloads a previously declared package via FetchContent and exports the variables # `${PACKAGE}_SOURCE_DIR` and `${PACKAGE}_BINARY_DIR` to the parent scope function(cpm_fetch_package PACKAGE DOWNLOAD_ONLY populated) set(${populated} FALSE PARENT_SCOPE ) if(${CPM_DRY_RUN}) cpm_message(STATUS "${CPM_INDENT} Package ${PACKAGE} not fetched (dry run)") return() endif() FetchContent_GetProperties(${PACKAGE}) string(TOLOWER "${PACKAGE}" lower_case_name) if(NOT ${lower_case_name}_POPULATED) if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.30.3") if(DOWNLOAD_ONLY) # MakeAvailable will call add_subdirectory internally which is not what we want when # DOWNLOAD_ONLY is set. Populate will only download the dependency without adding it to the # build FetchContent_Populate( ${PACKAGE} SOURCE_DIR "${CPM_FETCHCONTENT_BASE_DIR}/${lower_case_name}-src" BINARY_DIR "${CPM_FETCHCONTENT_BASE_DIR}/${lower_case_name}-build" SUBBUILD_DIR "${CPM_FETCHCONTENT_BASE_DIR}/${lower_case_name}-subbuild" ${ARGN} ) else() FetchContent_MakeAvailable(${PACKAGE}) endif() else() FetchContent_Populate(${PACKAGE}) endif() set(${populated} TRUE PARENT_SCOPE ) endif() cpm_store_fetch_properties( ${CPM_ARGS_NAME} ${${lower_case_name}_SOURCE_DIR} ${${lower_case_name}_BINARY_DIR} ) set(${PACKAGE}_SOURCE_DIR ${${lower_case_name}_SOURCE_DIR} PARENT_SCOPE ) set(${PACKAGE}_BINARY_DIR ${${lower_case_name}_BINARY_DIR} PARENT_SCOPE ) endfunction() # splits a package option function(cpm_parse_option OPTION) string(REGEX MATCH "^[^ ]+" OPTION_KEY "${OPTION}") string(LENGTH "${OPTION}" OPTION_LENGTH) string(LENGTH "${OPTION_KEY}" OPTION_KEY_LENGTH) if(OPTION_KEY_LENGTH STREQUAL OPTION_LENGTH) # no value for key provided, assume user wants to set option to "ON" set(OPTION_VALUE "ON") else() math(EXPR OPTION_KEY_LENGTH "${OPTION_KEY_LENGTH}+1") string(SUBSTRING "${OPTION}" "${OPTION_KEY_LENGTH}" "-1" OPTION_VALUE) endif() set(OPTION_KEY "${OPTION_KEY}" PARENT_SCOPE ) set(OPTION_VALUE "${OPTION_VALUE}" PARENT_SCOPE ) endfunction() # guesses the package version from a git tag function(cpm_get_version_from_git_tag GIT_TAG RESULT) string(LENGTH ${GIT_TAG} length) if(length EQUAL 40) # GIT_TAG is probably a git hash set(${RESULT} 0 PARENT_SCOPE ) else() string(REGEX MATCH "v?([0123456789.]*).*" _ ${GIT_TAG}) set(${RESULT} ${CMAKE_MATCH_1} PARENT_SCOPE ) endif() endfunction() # guesses if the git tag is a commit hash or an actual tag or a branch name. function(cpm_is_git_tag_commit_hash GIT_TAG RESULT) string(LENGTH "${GIT_TAG}" length) # full hash has 40 characters, and short hash has at least 7 characters. if(length LESS 7 OR length GREATER 40) set(${RESULT} 0 PARENT_SCOPE ) else() if(${GIT_TAG} MATCHES "^[a-fA-F0-9]+$") set(${RESULT} 1 PARENT_SCOPE ) else() set(${RESULT} 0 PARENT_SCOPE ) endif() endif() endfunction() function(cpm_prettify_package_arguments OUT_VAR IS_IN_COMMENT) set(oneValueArgs NAME FORCE VERSION GIT_TAG DOWNLOAD_ONLY GITHUB_REPOSITORY GITLAB_REPOSITORY BITBUCKET_REPOSITORY GIT_REPOSITORY SOURCE_DIR FIND_PACKAGE_ARGUMENTS NO_CACHE SYSTEM GIT_SHALLOW EXCLUDE_FROM_ALL SOURCE_SUBDIR ) set(multiValueArgs URL OPTIONS DOWNLOAD_COMMAND) cmake_parse_arguments(CPM_ARGS "" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) foreach(oneArgName ${oneValueArgs}) if(DEFINED CPM_ARGS_${oneArgName}) if(${IS_IN_COMMENT}) string(APPEND PRETTY_OUT_VAR "#") endif() if(${oneArgName} STREQUAL "SOURCE_DIR") string(REPLACE ${CMAKE_SOURCE_DIR} "\${CMAKE_SOURCE_DIR}" CPM_ARGS_${oneArgName} ${CPM_ARGS_${oneArgName}} ) endif() string(APPEND PRETTY_OUT_VAR " ${oneArgName} ${CPM_ARGS_${oneArgName}}\n") endif() endforeach() foreach(multiArgName ${multiValueArgs}) if(DEFINED CPM_ARGS_${multiArgName}) if(${IS_IN_COMMENT}) string(APPEND PRETTY_OUT_VAR "#") endif() string(APPEND PRETTY_OUT_VAR " ${multiArgName}\n") foreach(singleOption ${CPM_ARGS_${multiArgName}}) if(${IS_IN_COMMENT}) string(APPEND PRETTY_OUT_VAR "#") endif() string(APPEND PRETTY_OUT_VAR " \"${singleOption}\"\n") endforeach() endif() endforeach() if(NOT "${CPM_ARGS_UNPARSED_ARGUMENTS}" STREQUAL "") if(${IS_IN_COMMENT}) string(APPEND PRETTY_OUT_VAR "#") endif() string(APPEND PRETTY_OUT_VAR " ") foreach(CPM_ARGS_UNPARSED_ARGUMENT ${CPM_ARGS_UNPARSED_ARGUMENTS}) string(APPEND PRETTY_OUT_VAR " ${CPM_ARGS_UNPARSED_ARGUMENT}") endforeach() string(APPEND PRETTY_OUT_VAR "\n") endif() set(${OUT_VAR} ${PRETTY_OUT_VAR} PARENT_SCOPE ) endfunction() mrc-1.3.16/cmake/VersionString.cmake000066400000000000000000000315011501502116100172510ustar00rootroot00000000000000# SPDX-License-Identifier: BSD-2-Clause # Copyright (c) 2021-2023 Maarten L. Hekkelman # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # 1. Redistributions of source code must retain the above copyright notice, this # list of conditions and the following disclaimer # 2. Redistributions in binary form must reproduce the above copyright notice, # this list of conditions and the following disclaimer in the documentation # and/or other materials provided with the distribution. # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # This cmake extension writes out a revision.hpp file in a specified directory. # The file will contain a C++ inline function that can be used to write out # version information. cmake_minimum_required(VERSION 3.15) # We want the revision.hpp file to be updated whenever the status of the # git repository changes. Use the same technique as in GetGitRevisionDescription.cmake # from https://github.com/rpavlik/cmake-modules #[=======================================================================[.rst: .. command:: write_version_header Write a file named revision.hpp containing version info:: write_version_header( [FILE_NAME ] [LIB_NAME ] ) This command will generate the code to write a file name revision.hpp in the directory ````. ``FILE_NAME`` Specify the name of the file to create, default is ``revision.hpp``. ``LIB_NAME`` Specify the library name which will be used as a prefix part for the variables contained in the revision file. #]=======================================================================] # Record the location of this module now, not at the time the CMakeLists.txt # is being processed get_filename_component(_current_cmake_module_dir ${CMAKE_CURRENT_LIST_FILE} PATH) # First locate a .git file or directory. function(_get_git_dir _start_dir _variable) set(cur_dir "${_start_dir}") set(git_dir "${_start_dir}/.git") while(NOT EXISTS "${git_dir}") # .git dir not found, search parent directories set(prev_dir "${cur_dir}") get_filename_component(cur_dir "${cur_dir}" DIRECTORY) if(cur_dir STREQUAL prev_dir OR cur_dir STREQUAL ${_start_dir}) # we are not in git since we either hit root or # the ${_start_dir} which should be the top set(${_variable} "" PARENT_SCOPE) return() endif() set(git_dir "${cur_dir}/.git") endwhile() set(${_variable} "${git_dir}" PARENT_SCOPE) endfunction() # Locate the git refspec hash and load the hash # This code locates the file containing the git refspec/hash # and loads it. Doing it this way assures that each time the git # repository changes the revision.hpp file gets out of date. function(_get_git_hash _data_dir _variable) # Be pessimistic set(_variable "" PARENT_SCOPE) # Load git package if needed if(NOT GIT_FOUND) find_package(Git QUIET) endif() # And fail if not found if(NOT GIT_FOUND) return() endif() # Locate the nearest .git file or directory _get_git_dir(${CMAKE_CURRENT_SOURCE_DIR} GIT_DIR) # And fail if not found if("${GIT_DIR}" STREQUAL "") return() endif() # Check if the current source dir is a git submodule or a worktree. # In both cases .git is a file instead of a directory. # if(IS_DIRECTORY ${GIT_DIR}) set(HEAD_SOURCE_FILE "${GIT_DIR}/HEAD") else() # The following git command will return a non empty string that # points to the super project working tree if the current # source dir is inside a git submodule. # Otherwise the command will return an empty string. # execute_process( COMMAND "${GIT_EXECUTABLE}" rev-parse --show-superproject-working-tree WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" OUTPUT_VARIABLE out ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) if(NOT "${out}" STREQUAL "") # If out is not empty, GIT_DIR/CMAKE_CURRENT_SOURCE_DIR is in a submodule file(READ ${GIT_DIR} submodule) string(REGEX REPLACE "gitdir: (.*)$" "\\1" GIT_DIR_RELATIVE ${submodule}) string(STRIP ${GIT_DIR_RELATIVE} GIT_DIR_RELATIVE) get_filename_component(SUBMODULE_DIR ${GIT_DIR} PATH) get_filename_component(GIT_DIR ${SUBMODULE_DIR}/${GIT_DIR_RELATIVE} ABSOLUTE) set(HEAD_SOURCE_FILE "${GIT_DIR}/HEAD") else() # GIT_DIR/CMAKE_CURRENT_SOURCE_DIR is in a worktree file(READ ${GIT_DIR} worktree_ref) # The .git directory contains a path to the worktree information directory # inside the parent git repo of the worktree. # string(REGEX REPLACE "gitdir: (.*)$" "\\1" git_worktree_dir ${worktree_ref}) string(STRIP ${git_worktree_dir} git_worktree_dir) _get_git_dir("${git_worktree_dir}" GIT_DIR) set(HEAD_SOURCE_FILE "${git_worktree_dir}/HEAD") endif() endif() # Fail if the 'head' file was not found if(NOT EXISTS "${HEAD_SOURCE_FILE}") return() endif() # Make a copy of the head file set(HEAD_FILE "${_data_dir}/HEAD") configure_file("${HEAD_SOURCE_FILE}" "${HEAD_FILE}" COPYONLY) # Now we create a cmake file that will read the contents of this # head file in the appropriate way file(WRITE "${_data_dir}/grab-ref.cmake.in" [[ set(HEAD_HASH) file(READ "@HEAD_FILE@" HEAD_CONTENTS LIMIT 1024) string(STRIP "${HEAD_CONTENTS}" HEAD_CONTENTS) if(HEAD_CONTENTS MATCHES "ref") # named branch string(REPLACE "ref: " "" HEAD_REF "${HEAD_CONTENTS}") if(EXISTS "@GIT_DIR@/${HEAD_REF}") configure_file("@GIT_DIR@/${HEAD_REF}" "@VERSION_STRING_DATA@/head-ref" COPYONLY) else() configure_file("@GIT_DIR@/packed-refs" "@VERSION_STRING_DATA@/packed-refs" COPYONLY) file(READ "@VERSION_STRING_DATA@/packed-refs" PACKED_REFS) if(${PACKED_REFS} MATCHES "([0-9a-z]*) ${HEAD_REF}") set(HEAD_HASH "${CMAKE_MATCH_1}") endif() endif() else() # detached HEAD configure_file("@GIT_DIR@/HEAD" "@VERSION_STRING_DATA@/head-ref" COPYONLY) endif() if(NOT HEAD_HASH) file(READ "@VERSION_STRING_DATA@/head-ref" HEAD_HASH LIMIT 1024) string(STRIP "${HEAD_HASH}" HEAD_HASH) endif() ]]) configure_file("${VERSION_STRING_DATA}/grab-ref.cmake.in" "${VERSION_STRING_DATA}/grab-ref.cmake" @ONLY) # Include the aforementioned file, this will define # the HEAD_HASH variable we're looking for include("${VERSION_STRING_DATA}/grab-ref.cmake") set(${_variable} "${HEAD_HASH}" PARENT_SCOPE) endfunction() # Create a revision file, containing the current git version info, if any function(write_version_header dir) set(flags ) set(options LIB_NAME FILE_NAME) set(sources ) cmake_parse_arguments(VERSION_STRING_OPTION "${flags}" "${options}" "${sources}" ${ARGN}) # parameter check if(NOT IS_DIRECTORY ${dir}) message(FATAL_ERROR "First parameter to write_version_header should be a directory where the final revision.hpp file will be placed") endif() if(VERSION_STRING_OPTION_FILE_NAME) set(file_name "${VERSION_STRING_OPTION_FILE_NAME}") else() set(file_name "revision.hpp") endif() # Where to store intermediate files set(VERSION_STRING_DATA "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/VersionString") if(NOT EXISTS "${VERSION_STRING_DATA}") file(MAKE_DIRECTORY "${VERSION_STRING_DATA}") endif() # Load the git hash using the wizzard-like code above. _get_git_hash("${VERSION_STRING_DATA}" GIT_HASH) # If git was found, fetch the git description string if(GIT_HASH) execute_process( COMMAND "${GIT_EXECUTABLE}" describe --dirty --match=build WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" RESULT_VARIABLE res OUTPUT_VARIABLE out ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) if(res EQUAL 0) set(REVISION_STRING "${out}") endif() endif() # Check the revision string, if it matches we fill in the required info if(REVISION_STRING MATCHES "build-([0-9]+)-g([0-9a-f]+)(-dirty)?") set(BUILD_NUMBER ${CMAKE_MATCH_1}) if(CMAKE_MATCH_3) set(REVISION_GIT_TAGREF "${CMAKE_MATCH_2}*") else() set(REVISION_GIT_TAGREF "${CMAKE_MATCH_2}") endif() string(TIMESTAMP REVISION_DATE_TIME "%Y-%m-%dT%H:%M:%SZ" UTC) else() set(REVISION_GIT_TAGREF "") set(BUILD_NUMBER 0) set(REVISION_DATE_TIME "") endif() if(VERSION_STRING_OPTION_LIB_NAME) set(VAR_PREFIX "${VERSION_STRING_OPTION_LIB_NAME}") set(IDENT_PREFIX "${VERSION_STRING_OPTION_LIB_NAME}_") set(BOOL_IS_MAIN "false") else() set(VAR_PREFIX "") set(IDENT_PREFIX "") set(BOOL_IS_MAIN "true") endif() # # And finally, write out the header file # file(WRITE "${VERSION_STRING_DATA}/${file_name}.in" [[// This file was generated by VersionString.cmake # #pragma once # #include # constexpr const char k@VAR_PREFIX@ProjectName[] = "@PROJECT_NAME@"; # constexpr const char k@VAR_PREFIX@VersionNumber[] = "@PROJECT_VERSION@"; # constexpr int k@VAR_PREFIX@BuildNumber = @BUILD_NUMBER@; # constexpr const char k@VAR_PREFIX@RevisionGitTag[] = "@REVISION_GIT_TAGREF@"; # constexpr const char k@VAR_PREFIX@RevisionDate[] = "@REVISION_DATE_TIME@"; # #ifndef VERSION_INFO_DEFINED # #define VERSION_INFO_DEFINED 1 # namespace version_info_v1 # { # class version_info_base # { # public: # static void write(std::ostream &os, bool verbose) # { # auto &s_head = head(); # if (s_head != nullptr) # write(s_head, os, verbose); # } # protected: # version_info_base(const char *name, const char *version, int build_number, const char *git_tag, const char *revision_date) # : m_name(name) # , m_version(version) # , m_build_number(build_number) # , m_git_tag(git_tag) # , m_revision_date(revision_date) # { # auto &s_head = head(); # m_next = s_head; # s_head = this; # } # static void write(const version_info_base *inst, std::ostream &os, bool verbose) # { # if (inst->m_next) # { # write(inst->m_next, os, verbose); # if (not verbose) # return; # os << '-' << std::endl; # } # os << inst->m_name << " version " << inst->m_version << std::endl; # if (verbose) # { # if (inst->m_build_number != 0) # { # os << "build: " << inst->m_build_number << ' ' << inst->m_revision_date << std::endl; # if (inst->m_git_tag[0] != 0) # os << "git tag: " << inst->m_git_tag << std::endl; # } # } # } # using version_info_ptr = version_info_base *; # static version_info_ptr &head() # { # static version_info_ptr s_head = nullptr; # return s_head; # } # const char *m_name; # const char *m_version; # int m_build_number; # const char *m_git_tag; # const char *m_revision_date; # version_info_base *m_next = nullptr; # }; # template # class version_info : public version_info_base # { # public: # using implementation_type = T; # version_info(const char *name, const char *version, int build_number, const char *git_tag, const char *revision_date) # : version_info_base(name, version, build_number, git_tag, revision_date) # { # } # struct register_object # { # register_object() # { # static implementation_type s_instance; # } # }; # template struct reference_object; # static register_object s_registered_object; # static reference_object s_referenced_object; # }; # template typename version_info::register_object version_info::s_registered_object; # } # inline void write_version_string(std::ostream &os, bool verbose) # { # version_info_v1::version_info_base::write(os, verbose); # } # #endif # class version_info_@IDENT_PREFIX@impl : public version_info_v1::version_info # { # public: # version_info_@IDENT_PREFIX@impl() # : version_info(k@VAR_PREFIX@ProjectName, k@VAR_PREFIX@VersionNumber, k@VAR_PREFIX@BuildNumber, k@VAR_PREFIX@RevisionGitTag, k@VAR_PREFIX@RevisionDate) # { # } # }; # ]]) # configure_file("${VERSION_STRING_DATA}/${file_name}.in" "${dir}/${file_name}" @ONLY) configure_file("${_current_cmake_module_dir}/revision.hpp.in" "${dir}/${file_name}" @ONLY) endfunction() mrc-1.3.16/cmake/mrc-config.cmake000066400000000000000000000204011501502116100164560ustar00rootroot00000000000000# SPDX-License-Identifier: BSD-2-Clause # Copyright (c) 2021 Maarten L. Hekkelman # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # 1. Redistributions of source code must retain the above copyright notice, this # list of conditions and the following disclaimer # 2. Redistributions in binary form must reproduce the above copyright notice, # this list of conditions and the following disclaimer in the documentation # and/or other materials provided with the distribution. # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #[=======================================================================[.rst: Findmrc ------- The module defines the following variables: ``MRC_EXECUTABLE`` Path to mrc executable. ``mrc_FOUND``, ``MRC_FOUND`` True if the mrc executable was found. ``MRC_VERSION_STRING`` The version of mrc found. Additionally, the following commands will be added: :command:`mrc_write_header` :command:`mrc_target_resources` Example usage: .. code-block:: cmake find_package(mrc) if(mrc_FOUND) message("mrc found: ${MRC_EXECUTABLE}") mrc_write_header(${CMAKE_CURRENT_BINARY_DIR}/mrsrc.hpp) mrc_target_resources(my-app RESOURCES rsrc/hello-world.txt) endif() #]=======================================================================] find_program(MRC_EXECUTABLE NAMES mrc DOC "mrc executable" ) if(CMAKE_HOST_WIN32) find_program(MRC_EXECUTABLE NAMES mrc PATHS $ENV{LOCALAPPDATA} PATH_SUFFIXES mrc/bin DOC "mrc executable" ) endif() mark_as_advanced(MRC_EXECUTABLE) # Retrieve the version number execute_process(COMMAND ${MRC_EXECUTABLE} --version OUTPUT_VARIABLE mrc_version ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) if(mrc_version MATCHES "^mrc version [0-9]") string(REPLACE "mrc version " "" MRC_VERSION_STRING "${mrc_version}") endif() unset(mrc_version) find_package(PackageHandleStandardArgs REQUIRED) find_package_handle_standard_args(mrc REQUIRED_VARS MRC_EXECUTABLE VERSION_VAR MRC_VERSION_STRING) # internal, create an ELF template file based on the current compiler flags and all function(_mrc_create_elf_template _target) try_compile(BUILD_OK SOURCE_FROM_CONTENT my_code.cpp [[ extern "C" int my_global_int = 42; int main() { return 0; } ]] NO_CACHE NO_LOG COPY_FILE "${CMAKE_CURRENT_BINARY_DIR}/${_target}_rsrc_template.exe" ) if(NOT BUILD_OK) message(FATAL_ERROR "Failed to create template executable") endif() endfunction() #[=======================================================================[.rst: .. command:: mrc_target_resources Add resources to a target:: mrc_target_resources( [RSRC_FILE ] [DEPENDS_FILE ] [VERBOSE] [COFF_TYPE ] RESOURCES ... ) This command will specify that resources should be added to the target ````. ``RSRC_FILE`` Specify the resource file (object file) to create, default is based on ````. ``DEPENDS_FILE`` Specify the depends file to create, default is based on ````. ``VERBOSE`` Pass the --verbose to mrc so it will print out the list of files added as resource and the path to be used to access them inside the executable. ``CREATE_ELF_TEMPLATE`` Create a small executable to be used as template for the ELF object file generation. This is executable is built using the same compiler settings as the final project and thus contains the correct ELF header from which mrc can copy the necessary information. ``RESOURCES`` The files to pack into the resources of the executable. ```` here can be a file or a directory. Files will be added to the root with their filename. Directories will be added recursively, if the directory name ends with a slash character, the directory part of the name will be stripped. ``CREATE_ELF_TEMPLATE`` Create a small executable to be used as template for the ELF object file generation. This is executable is built using the same compiler settings as the final project and thus contains the correct ELF header from which mrc can copy the necessary information. #]=======================================================================] function(mrc_target_resources _target) set(flags VERBOSE CREATE_ELF_TEMPLATE) set(options COFF_TYPE RSRC_FILE DEPENDS_FILE) set(sources RESOURCES) cmake_parse_arguments(MRC_OPTION "${flags}" "${options}" "${sources}" ${ARGN}) if(NOT _target) message(FATAL_ERROR "TARGET option is missing") endif() if("${MRC_OPTION_RESOURCES}" STREQUAL "") if("${ARGN}" STREQUAL "") message(FATAL_ERROR "no RESOURCES specified") else() message(DEBUG "no RESOURCES specified, falling back to use ARGN") set(MRC_OPTION_RESOURCES ${ARGN}) endif() endif() set(_dir "${CMAKE_CURRENT_BINARY_DIR}/mrc-${_target}.dir") file(MAKE_DIRECTORY ${_dir}) if(MRC_OPTION_RSRC_FILE) set(RSRC_FILE ${MRC_OPTION_RSRC_FILE}) else() set(RSRC_FILE "${_dir}/rsrc.obj") endif() if(MRC_OPTION_RSRC_DEP_FILE) set(RSRC_DEP_FILE "${MRC_OPTION_DEPENDS_FILE}") else() set(RSRC_DEP_FILE "${_dir}/rsrc.d") endif() if(CMAKE_HOST_WIN32) if(MRC_OPTION_COFF_TYPE) list(APPEND OPTIONS "--coff=${MRC_OPTION_COFF_TYPE}") else() # Find out the processor type for the target if(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "AMD64") set(COFF_TYPE "x64") elseif(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "i386") set(COFF_TYPE "x86") elseif(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "ARM64") set(COFF_TYPE "arm64") else() message(FATAL_ERROR "Unsupported or unknown processor type ${CMAKE_SYSTEM_PROCESSOR}") endif() list(APPEND OPTIONS "--coff=${COFF_TYPE}") endif() endif() if(${MRC_OPTION_VERBOSE}) list(APPEND OPTIONS "--verbose") endif() if(CMAKE_CROSSCOMPILING OR ${MRC_OPTION_CREATE_ELF_TEMPLATE}) set(TEMPLATE "${_dir}/template.exe") _mrc_create_elf_template(${TEMPLATE}) list(APPEND OPTIONS "--elf-template=${TEMPLATE}") endif() if(CMAKE_CROSSCOMPILING OR ${MRC_OPTION_CREATE_ELF_TEMPLATE}) _mrc_create_elf_template(${_target}) list(APPEND MRC_OPTION_RESOURCES "--elf-template=${CMAKE_CURRENT_BINARY_DIR}/${_target}_rsrc_template.exe") endif() cmake_policy(SET CMP0116 NEW) # If we can use DEPFILE, use it. if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.21") add_custom_target( "generate_${_target}_depends_file" BYPRODUCTS ${RSRC_DEP_FILE} COMMENT "Generating depends file for ${_target} resources" COMMAND ${MRC_EXECUTABLE} -d ${RSRC_DEP_FILE} -o ${RSRC_FILE} ${MRC_OPTION_RESOURCES} VERBATIM) add_custom_command(OUTPUT ${RSRC_FILE} DEPFILE ${RSRC_DEP_FILE} DEPENDS "generate_${_target}_depends_file" COMMAND ${MRC_EXECUTABLE} -o ${RSRC_FILE} ${OPTIONS} ${MRC_OPTION_RESOURCES} VERBATIM) else() message(STATUS "Not using dependency file since cmake version is too old") add_custom_command(OUTPUT ${RSRC_FILE} COMMAND ${MRC_EXECUTABLE} -o ${RSRC_FILE} ${OPTIONS} ${MRC_OPTION_RESOURCES} VERBATIM) endif() target_sources(${_target} PRIVATE ${RSRC_FILE}) endfunction() #[=======================================================================[.rst: .. command:: mrc_write_header Write out the header file needed to use resources in a C++ application. The argument specifies the file to create. #]=======================================================================] function(mrc_write_header _header) execute_process( COMMAND ${MRC_EXECUTABLE} --header --output ${_header} ) endfunction() mrc-1.3.16/cmake/revision.hpp.in000066400000000000000000000063771501502116100164240ustar00rootroot00000000000000// This file was generated by VersionString.cmake #pragma once #include constexpr const char k@VAR_PREFIX@ProjectName[] = "@PROJECT_NAME@"; constexpr const char k@VAR_PREFIX@VersionNumber[] = "@PROJECT_VERSION@"; constexpr int k@VAR_PREFIX@BuildNumber = @BUILD_NUMBER@; constexpr const char k@VAR_PREFIX@RevisionGitTag[] = "@REVISION_GIT_TAGREF@"; constexpr const char k@VAR_PREFIX@RevisionDate[] = "@REVISION_DATE_TIME@"; #ifndef VERSION_INFO_DEFINED #define VERSION_INFO_DEFINED 1 namespace version_info_v1_1 { class version_info_base { public: static void write_version_string(std::ostream &os, bool verbose) { auto s_main = registered_main(); if (s_main != nullptr) s_main->write(os, verbose); if (verbose) { for (auto lib = registered_libraries(); lib != nullptr; lib = lib->m_next) { os << '-' << std::endl; lib->write(os, verbose); } } } protected: version_info_base(const char *name, const char *version, int build_number, const char *git_tag, const char *revision_date, bool is_main) : m_name(name) , m_version(version) , m_build_number(build_number) , m_git_tag(git_tag) , m_revision_date(revision_date) { if (is_main) registered_main() = this; else { auto &s_head = registered_libraries(); m_next = s_head; s_head = this; } } void write(std::ostream &os, bool verbose) { os << m_name << " version " << m_version << std::endl; if (verbose) { if (m_build_number != 0) { os << "build: " << m_build_number << ' ' << m_revision_date << std::endl; if (m_git_tag[0] != 0) os << "git tag: " << m_git_tag << std::endl; } } } using version_info_ptr = version_info_base *; static version_info_ptr ®istered_main() { static version_info_ptr s_main = nullptr; return s_main; } static version_info_ptr ®istered_libraries() { static version_info_ptr s_head = nullptr; return s_head; } const char *m_name; const char *m_version; int m_build_number; const char *m_git_tag; const char *m_revision_date; version_info_base *m_next = nullptr; }; template class version_info : public version_info_base { public: using implementation_type = T; version_info(const char *name, const char *version, int build_number, const char *git_tag, const char *revision_date, bool is_main) : version_info_base(name, version, build_number, git_tag, revision_date, is_main) { } struct register_object { register_object() { static implementation_type s_instance; } }; template struct reference_object; static register_object s_registered_object; static reference_object s_referenced_object; }; template typename version_info::register_object version_info::s_registered_object; } // namespace version_info_v1_1 inline void write_version_string(std::ostream &os, bool verbose) { version_info_v1_1::version_info_base::write_version_string(os, verbose); } #endif class version_info_@IDENT_PREFIX@impl : public version_info_v1_1::version_info { public: version_info_@IDENT_PREFIX@impl() : version_info(k@VAR_PREFIX@ProjectName, k@VAR_PREFIX@VersionNumber, k@VAR_PREFIX@BuildNumber, k@VAR_PREFIX@RevisionGitTag, k@VAR_PREFIX@RevisionDate, @BOOL_IS_MAIN@) { } };mrc-1.3.16/doc/000077500000000000000000000000001501502116100131205ustar00rootroot00000000000000mrc-1.3.16/doc/mrc-manual.pdf000066400000000000000000000640561501502116100156620ustar00rootroot00000000000000%PDF-1.4 %Ēģ¢ %%Invocation: path/gs -P- -dSAFER -dCompatibilityLevel=1.4 -q -P- -dNOPAUSE -dBATCH -sDEVICE=pdfwrite -sstdout=? -sOutputFile=? -P- -dSAFER -dCompatibilityLevel=1.4 ? 5 0 obj <> stream xœ„WėnGžļ§Ń]vŲ™½S©R”T¢¢@‰ū£JP5^ć…½˜½„Dm߀>J߱ߙٵ×NIBQ¤$öœ9÷ļ;gŽ3 ęŃĻš7+g;Ÿ½Ÿ ó-žd%{¼˜=|Ó‹õĢ K§>OR¶(g"öx"ŁāĆģŌ)›ģĢgsök«›¹+C{"tžŌe©ŖUĖĘół›ÅO3×ćR@E ),š4$#~ÄĻgĪ‹ļē_Ć)œŠ‡©ƋÕĢłłé|ńvā’ōxąÅĢ’Ž=rƒ¼`’°ļY£Ūŗo2ͲŗÜę<2W'¶\sEÄ£ūä·/_<;9“`DŒv£ųtÆÄŲÜO<1xēևwīūž— §]/¹ußmūīČlĢĆAĄ,ōqĆyóäĪ«#ü©ĶShoę"įž'N]w&ó“ōE<GQe%E(œó7¶D> Hį}¾-“{|r2ķnńOĢÓT¤ŽžxyäBģóč∧ž=€ŗX»„Ź6y„󉬉›"$īé`'+TŪZ |īłirƒ ā~”¦Ÿcg„:udFŲ¤Ü`&ä2ŗc“ fŌ2?īüIo’w{¤<¾k+VžVēĒ9£Ģߜ³'b’3@A°Sü–œs ØrepG”nŽQšĄ~pJ7ŗŲ‘ĻˆŠ˜1rNnŽŁ‘‹Ļ§ BŁÅ<åpXųŽnڼ®ŽźˆbĻ?š-JÉpų¾!^µ²œ)AE†¦'1ʽł®$ĆŌŠ0²‰Ó“MĘM¢ ±š`ū«¼ŹŠ~„ٽŅJŻ3b†ī„į’‹zä’”ÉWl]×g] ƒ±%ž oĀń#M"h~ōˆ~3ĀĻ™sļ”Ā覇ž‹l$cē²ćŻewļlž­I”“ÆŁ™cÄēģŲ'Ōoōåm×hU²¼å¾%Ćśāī^ž/Ū‡ŸfOĀĆÉōłįéɓ×Ļ^-ž½|ńÉō³ŖčĢē"JL®.ö%»R»JÉ#°°=šxD☌Ūm‘gŖCĖ“45ūwÖöų^—ŗźTse²ÅŁb“·ę_„Źś¶WEqÅŚ®nōŠÕ[åķ;¦ZŒXćx ĮŽZl¬÷…j`Ś’>#éś1 ģ=c@‘ZH€œĖ(‘ܧ,A‰‚WēšÕk֑KŪ¦ĪōŖo4łÕmTĒTÅŌ>.–©ŖŖ;Öęå/iųos8ŻÕ¬»nXQ²µénw“<]"NZśāŲ\Z&t.·:묪u_eFüŁź¦øšG!8+ˆn:‰:żłLp˜ĶhńĶ©ózŲFZ¦ą½bÄ©'}G]‘Źz\Š„›šB`ŗŠ»ŹbųOČŖ1U3³DŁņŠY|åÕ9Cن*Vm¾"IĶō€6kG“2vł C‰0õČśNĮ•ÕT•v:[Y°t(vˆłkKUˆe@Q‡Ŗ.ß"„¦EpŽJēy½DŒ«ĘuŹ?®äÕŹ6›nÄčVģY·.§-Kp½Bł­köŹĮ’Ø2”¢E5×M]äÕq/õ-¼9$IŚvƑZŸÜæ44Øģ±ŁŠü×”/eLÅŻƒ’„Įż§7Ļ›ė—n×ł}7Ų>ī)…ĘĆ8¦ĀfVÜŪ‘Z*¶Õ8Rņõ•©”5C5~`>£!ś¢£2ć+$ ”"óūf`WuoŠXäÕ;`ć\ܲTt`ņĄN [RÜjZ*Æ źŃń_O…föoé÷ƒ6?L  ”óPī‹wŪŽķG»fµœvź,lS>ŪŃōča«ŗ gæĶ…°oØ!#ķ$³ lA®é,;jsgP@”`ü ꁌ*5ølƒ>ī‚š>„¾x‘w*˜čžGG¼Š§”%¬zk@†AM×1éŌyQ/UĮ.ę2Än˜SUز{~hµ”āCś_/KČҧć]‹§ōd„8cTźõœŹć é(45q!¦bŪ—p‘zŲp|"SAcŒ"xOķߦŌīc؃{,ļXÓcD#(öŲ±2єHūŌ·7äMŖ¢`ŖŠĢx Ą€1sC,_·,ōž` z™Éąė;™$Ų”.=)‘" ßÓÅģüü P%Ķendstream endobj 6 0 obj 1772 endobj 15 0 obj <> stream xœ„XŪnÜČ}×W4übN ¶ŲĶ{€<ČkńB†7Öl`“zȞ®9ä,/ Č'x’9U}įͲ 0lYdw]NUŖāÄ£ŒxųĒüĢŽŁ_üqĮŌSb~dGņf}qõ9ĘėŻ…>ĢHĢIœś4IÉśxĮb&œ¬ĻēŲdw»[‘_[٬\zŌc”óS}<Š*o‰}æśmż3ČM\?".ü]ēŽėŹrē梫õļÕ£©ļ”xŖź[P'ÜŸ{ qĒWŚā¬’¼»yOP‘U^ˆŖ’mKŗšō­¼$ķ”ī˜l%‘EwŠĀ®>3fōq/¤<“śŲܘ„rꄁ~‰µŹ]ݐ²čŗRŗZ!¹sžvsū𨷿ˬkI½ŌĆŸśAhōšgō(ńŪbļ*·ż(hG·•£ÖS­TKµh#Ō.‹h2\l‹¹j?¢~ņŽ>÷ā ąžo¢o!’tK®ß| Šż€6 L䊔ĶAä<ˆ¬#÷+Š4ńBG”½$č]w(Zum NY×_W ”xAźČœō'RT&}}ÄY¤Ż8ąØrčAć4D³iĒŌ·Ó·č–‡‡só\īšyL£(M¹“ēėH‹¾ģHÉd‚ĮhĢƒ±qĄqå78:óQ’¬oYuD4Ł”č D}#éŹ =Ϲsž¹bŒN‘¾SļˆŖšīMQõ—$µææo¤|sūönEõTˆ×vQS¬N“ļb •ň©u̹¾Į iāĒ&Aõń"ƒ6CŌŪ®n$„ ż]¤|āC°‚dˆÖæ’*˜`¢ödČCN½4Jß$d Ņ™L;H‘/lN!dqb—®§Ń āx“7ŃQ”ˠh²ŗź„¶—¼Ćō½_1ČĻŌćŽlŚ¢®qeœĖŒ|/˜×ŽõēDŅż µ–Õ»Ż<x&ņlÖu'ąūéQ ¢€j¾d„ė¤>uhüÆ=ɬų&óK"ČOŸŽæ'ßJ‰Ļ³FŠjS“NAį<ńh:Ø,Ŗ\e1E80,Ąiž[JÖ:†~Jn”Ż8¢Y±ĀCŲś#äó²,¦ ö“…k£ė®§Ō÷@޵_W˜žs@£,xé! ę"™-ėĖł5ūų!‰ĄkóĪr³½1Hʖ#šćRi ģ`Smy†('³čé:%Ś1ČašŽ5Ę÷\”%95ŅÅŅ>Nc/“į86­³“ņ›čG‡Ŗs0C4yņxBc‹EŪåuß-,…œŒ ēC‘ČcŻ«ź97ĄcX÷yŃ~%ŠėUĀĮļ"Ė ė*–di IĪ£ŻČ¶ī8Cqfo»ĆHÖ²ø† ėŗ`Śé;ė&ŌȁöĮ,”k¢j;ÕęD|lŸĶ\)ōe5X„÷ĖÖ<}ūŸ’6&.\K! ę‹lėV.£‚0äŲoĘ0E;§ó‹Š~.·+®ę©Ōé÷Dcp‰8ļś}ģ‘}%g˜e$4vņiuÓTĢfį~¶a,ķFö[Ś-ͻǖk˜R·•ŗé{±ļ<Ö:ēÄēQ?,QOT?zuUgåiɲ3ˆ±a[_ųŠĒ¤ ^'m3kó8qBM.O;Éµ(„JĮG?U™ž ٬!Gģ‹pTM¢E••}.—ŃĮ÷”¼- j°”G©»°"<'¢%gY–”\C ĶĪqHƒa|Ł÷„Š›}>° ēše”²Ź§éQ×ķÆƒ1L—Śä]ŻĆy8œۊ™żŸµŽčb_’eć̤ŖĪB˲Ęé\T{ŅōØHœNåć_õü ¼ūYŖ– Y©ŻÓ6 L÷<—ł‹NtØž Pj}’J„‚WGö!³>|Š÷ķk“ƒäÖTb\½†é2Ā0^ą4ĶżtĆÜÓ4d̾™Ću5A6ĀŒÕŠŗš“GĆŲ7‡ļå %„|Ģ|4S³'ęHė”īfb‚HAŽSźüF¶˜cIÉÕx]ž÷l2ķ§LN3xĶ(ņ8nœõČU/šœ“„h°•\ĮJ²Ņ– Ŗ0Ļk8VA1¹“\ M0§¾P“^/”&ģ#ÄŖ}B՜J‘-RÄzn&?x«Œ›ß€ėFµ•LS5{QxŖy‡bĒś„ 2lšī_×¹ywū£Fļü(śµšÄ؊ēøčŁŅmE9­e㛢U‰Ż·bK„h„^ķ\ŗ÷ü !Į¢Mܚ<К݁õ»‡/Ž €žŖ½É™UMģūRŒ0h½vĘAÜ#…¦¦Kˆ÷”µ,˜ŖĪĪz0ń. “%Ķ [VEŹ£aļ2󀙊m9©[r¼ŗZųōJ÷ĶJ-āółƒ¬Ī>æBƒOČS³Ša K慂Ł@Ŭ"\ xģ˜FĄą÷0~ūāøæś?šžŸ`Ń)'\÷r#:{‚X«ü”ĖŽ†‹‰ZyZh’F>V4üÕ1ē¹ĮƒÅ%ō­7õÄMo²„”DĶgh™mSž<ī!ŻĖJŌŚļ2ō@Õššˆć£K 0ćw‰ń«P¤×™e›Õå>YžŽd[#ŻL0¬`h)fÄPŪšŠ 9†Ŗé+$ ŻæĢցm‚s|É-Š…Ž®ĀĶœć°‘†Ī}Q÷ķ%1c_̜¾CN”ļ×*ż ¦Ā@sHDĆa7k«Ubü$rj½—)Ųį—ćF%†oB¢ßA£›@Čh4~ßhy¬›€NaVčš{aZ{q Šv†<Ö d5 xbL Ŗ7¶4¹T©ż/&†$½`ųp3ŽLbómÓŁłL§e9 ¢aq“„łäaĆŗ“ Š|ņłuųäc§^=õ«ZwĪbčNb:®oväżpŗ*˜ńƒaćÜ­ŗŌÅY¤ådÜńĶf ŗaPk1 l¬0“!žŸō˜ÅMÕ:Ķ=ü@aąłv:į瀣 I}Ų|·¾ųüł/ŸÆendstream endobj 16 0 obj 2273 endobj 19 0 obj <> stream xœXŪŽćĘ}×Wō›©`ÕÓ^wƒŽxl0H`[~0ցAQ­½)“Ō\ų&’œSŻl’ŅĢŽĮ<Ģ Ł]}ŖźŌ©jžŹ—LŠĻš»8,»Yüŗö)~ö~½øś.”ėŻĀ-–,Q,É4O3¶>,d"xŖŲśnń)8“ÅOüiÉ~čL»\©Hp!£ąÆĶį×ŪŽł÷ĖÆ’»ģņ,"Ó:fėėÅśOŸ‚o>¬—öi•_¼ž~Xė1`įJāøķBr”BwīzoŲŻ2ćYé(ČXæ/;<‘t~Ŗƒ¦żÜ1<é÷yO ŲcŪõ¬hŖŹ}ĒņŖbŪ¼ĻŁ®mXdŲce:ÖMQ>šķVÖEuŚ–õĶōrלź-^Xxčz!x”&ˆÜš›Ł¶lqHӖ¦ćĖU„<βąėšuåįX˜ŲkDsAŪ‹…ŒąZœ"ŌšņSp_.µĄ®T]aW Z„WiŗUttJG­É{œŚ7äU³\’bķ¦“#Ņ\ź(s;‚»=½ī@®E2Xš6o‹}YäĖ‹Āt™#Ļ)HƒIŚ°ŪøŁŠ‹• %O²1MHA[h’ĶūŽŌ†¢fķµfŲŽõķ©čO-Ā š©Ī°¼ENŪ’¶ Ē^G"‚ 3 ¼įdČ-šLQÜņīŠČ¢č²©Y³³.5›_š€rŹł3Į"k+•h®Bå ¹Ž¤5³©šMgA’ēģoŁn\E6Ŗ"ä”Üęcø3.Ó,^E[=AYåš!ļŲ lē[³‡ęŌ²üx¬ ū®ģ÷xLw’„+1ęńę;ÓaūR¦\ ½Aa>‚k÷#—Ę7ēęV”Źx$F石ųO ø0˜Dć?Īł«|¢Cp6÷frAņLgŽ·ŗ'¹`{O(é6ƒŠ1Ņ1Z[$· ąd%äQģĶ `Ų5$äfQ½#Ū5ķ!ļß:ÓņĆW¬ķŚāgˆ™w ü® ~į©īŹ›”¢ŁįēŚśƒŠIFW\ĶŻ÷ļŲՕL<€ =uёˆōĀĢ‘vżLō$¶!ÄĮršī¾‡°n*ņŃ ŒC5čND¾±«¶ļ lBWW“${\¦ć×Ā"@®æÕå‘@„ÉAPACŚ>F8šNłĀ8Ųu¦÷’E )Žæ“—Ōv.lvåoÖ&5b‘M'øxüJ “žUØ\}js5ćĖļļ\ š„JdėJ¤Øņ®3VīžØ<ȤåD‰89Iß_Ļ&v=V®‹MŻē„si’Īc^\œ%Cœšł£ęt˜µ4Īh¬Z¾ž—ó9!.ZŖ ņZƒļ>>šåB µŌ4{­`/}õŪ·ž_ĢƒĻR¼t=zMÉsķœmņ®,Üévžp£€ŲĒźH\'6óÆśü³+t@“©«0±“Q@n@I`a‘y åŒTŸäŚuŻaóĻiŻĘ ŹS7Œcē!Iѱ¦Ą?3H Rcʦß7[?tø²˜ÓøHQD’AްœµōŪ2Š4B E¾óŅÉē@‘4¦P:Xtˆ’-§~łĮ-ŽXéTÓx6ĶÉY;©ōįFcĖI¬›Ž³ofs¹Å "ž ÷|1˜(N 4¦c\ŽļJĢÓ­Į@WŸ»Ā'ķÉ]ŸŖźŲ·ē5F¦1(OFÄ$MżyĻÅ •Å£XĢp°ßLŪpöćRZæ#?`Źj@%sįø½¹$ź¢é7G ĘÄóMÓTē'ĒĄ#.˜]ģMńŁŖįl¼ØŹķ0A_œ–Å“ū…*F…-у2ˆ[`ņĆfi=E·;ķ^Ŗi‘L5ķŹlM[ž1 KįEóvhșH±ÕL¶»¹Ā©1÷īM©ŹhhR>$]漜zĪ‹ńŠY\Õl,śšK…»aœ¦A?» /āPŽ“5ČD ĀŲ£ ĢfņB”ŹĢØ1ŚE —#ō934„D¾Ą āTś1Cń8Ńék˜øĢ^Į Ź“tqš}UY%³į x’ĆüĘ$ •„æ”ėŲ/¦q™”dʑłm”4“©«vƒ‹=ÄѰ×.ōbŗ]wX‡[2fāŚ bž\oPŒ¾` æb¼ąL}VŅP×¤‹YØ1ł”üŁT»„Œ¾’Ė%żŃOc5~šŽķ .ń-ŚhĘć8‡×©É«ŹlgW<מE ©ŗ¾A·›¬mˆ0øųÓFk¾šī… lŠĒŅÕK’鬹›q:½&1¤Ļ\ ;˜iĄ»ĮōŅ"Čuy> /Contents 5 0 R >> endobj 14 0 obj <> /Contents 15 0 R >> endobj 18 0 obj <> /Contents 19 0 R >> endobj 3 0 obj << /Type /Pages /Kids [ 4 0 R 14 0 R 18 0 R ] /Count 3 >> endobj 1 0 obj <> endobj 13 0 obj <> endobj 17 0 obj <> endobj 21 0 obj <> endobj 11 0 obj <> endobj 25 0 obj <> endobj 9 0 obj <> endobj 26 0 obj <> endobj 7 0 obj <> endobj 27 0 obj <> endobj 12 0 obj <> endobj 22 0 obj <>stream xœ•yp×ĒWÉ ”XXqŗė“„@ī„@i )—›Ć6±±-Ÿ’|Čŗ­[+­öé²,²$ˇlŒ±ĮŒcćp…„$˜£t(&ÓNi:É[²L§«Š™ęļĪūēĶŪyæ÷;¾ßĻr©S‡ó«ŒR©X¾šżšż’Ņ‚ųA*Ģ”_žB’šĖ¼Ę<ÉxņĘf `̘|yśD8ōtĶ‚Ŗē‘©ĪŪé»ēgīŲ½ 5õu•šźŅā’š”eK–¾™’ÆIłļ—”õbyi±,e»QŠ%•R±¬fK©4_!OŁQ!Ż/Kyöž/žģ’  ˆ@V)ÆŁÆ)—”J¤īE¹K—!ČVd;’d"D²õČd#ņņ>’ŽlF¶ 2d²IDȋlw©HgēčlŹw376uŚT?ÆĒšeüs =h'z½6mÉ“:č™I“–Qq˜ęŸb»3IŻ™#¤óą4aęF£IP‹ĶØ÷†šs0Ÿ'čńE¼ķĶ/ VvžiÓfł\씑ī]&…ćMH—pŠj"€ AŁQAI*ó=o’? —ŚO\¾šķ©ńA€¶L#Qf•ą¹Ģ*žĄgŲMĢ/‰›u‡F:Ə®`lB¦żŅq=ė!÷,żš0źjw… @'Ę/f’jVØÖÖn2ļÓó(T÷·$ähėŪ›ƒ7Ā7šNC›# P8.‚«į›·rG×§g—ÆŚŽ÷³HĮƒ­JįqgØ ģe+ņ6ļߚ-Ż Š UK{’a8»wļ¾|~lœDgŅ"CX”߈ŖĆ‰sķä9ל'T1Ėy­ü@Šėm Ķf«–Ų¢-d0łī…Ó7Ž*ēœĒÅ£ŗƒ †Š„Oōż”kå ^Cœ§ē[ō„£XÅ­ö±7ė«õnmņ»9yļ•GņŽeāG÷K€ Ż'•äī”§Ćfƒ¦1ųSŽŃk…«ł±ļ]nĄ.QÓC$e%°Ā½›÷”[Ŗ­6˜œ.½Ī_ĒĜŹAZEę2›˜QeD×}ņPģŃEĢąĻŚ¤Óé€ČBų›\ø¼ų}xw—ÄXv˜^Ųž¼ ÷\›#” įaEEy¹Éb"+QēƒkGį;ø§Įåvlž¤-6’4e[e‘< bDĢB&Yžv$’³+ĒĮēļćŹUN+ØFjŁclØ#ōĶ$ʬ†W…żĪv9(oļa¦o|ŒTŹKµ²äbEģäŁĮĒ'.āCēOö Ē'±ÉÓ’ sčŁCÜz®š€§ŽÕ†AHĪģ~w‚)b ³éōꊢš £A©0Y Ą©¾ö€ŁgØB«UTE4½ŸĮŁp̆ü2œ» ĘŠŽźNi®V”«7‡ XČä·=((^óóD“jģ„+kźą;)8€•*"v™ķ”Ō;晙h<Āé½ĶW¹šū„Ū YŻQzģź_?<{ļéjh“Łj“;ķ n38 vņ}G,źĶķß¼Τ0ó˜%oœĪx4łQļĶO°xäśÅ(ēą×Pżž9ĀuŪ$ŗ\€ī,8Óān®ļĆc^ųz䇹žč˜“k/3—YʬdRwzē­Æn_ścdp±°’Įõ›§i®•KŌÅ)^T"Ö¼„DYY1śvT·s˜×.Ńæä¦S„łĖŸ®T¤”eģ"£ÕšŗÜī­syAŃ•J¦¦¤æjäĮ—‡›aWį5äšėśx ĀļĖƜhs½ŻŹö‹tąv+eV¶Z]{4ŌÖ~¤Ø{ov”b_1&-P퓦¢‹ćšcņŪįżÜu“¹=±žĢ”[0õBŁķ9‚Ózp Ÿv·µ®²­¬A P†»tĶĀŻéł*|[FäĄPo éē§³ū}č†/>x ļ~üõŜć©]XncU+hA[¢]Mn7 ĪüÄÜd¾ŗ€›rSŖlµ’X:PɆĖÜ^°fuĪ‘Ļ:ź‚­!¬¹møÆž½üœO' CņœbcžŗWäK„+b£ßžūóēų/ł:|Ūs+ī"qœÆļ?óµ¾”±õÓąŽó(4 :Ń”ŹīĀōõé̼EœAgŲ}[õ…ÄvĖÖ.WŌÕfµ" ·Ė-2ŹéŠŪ•¬‘˜ä§–²CpQS’Š'·/z˜„l“Ńą žĮ²XĻE‚¦Ŗ .6~øūė ?ƒd”ž.Ģ›äĀjČV—V³Y«­5[jµśė<­ž6Ü×ęróÜ ÷į$é~U­& I6“ÓT€d)o%µ„:›IKŅm%d@…µŖzGzļžĆ”ō‡ĀJ•BRRötE£±žš®J<žd˜3|“ Õp–0}„ÕŖØŁ^w ŽÓäkĆ]^Ļ.ŌSéń²ÅĻŹž0Lhį“ā ö““4S`Õ}ż±æ}ŽĶ|Rōsy‰šĒ›p+•‘'ilƒv»ŻAhUE)äf›ž-Ėįkōzƒu¬į½.Ÿ»ųEÕŻe*•­VŽe•ēgo`¦tŻj>Bč@MGé–“÷™Wc甘tgš”Žń_ÜI €€ŻēōŚXŖå‹K˜Ų!(Žqč {ŠIž‡pYĖ‘ž‰s÷>>ÓŠ&æEg2Ó ˜Wp«Ģ”7d”ńI- Ÿ:ueģņų‰€6,, Ģv¾Ńš I-0³“’6ėŗFOŗžęóóā®Čė„kFąÓ>(›Ś™W.ѼÉ9żkSk!iP¬*eę6JTR÷ėķøsÓ>Ų­Öźˆ°ūźZļŃČ0\†/zŪ€e VD˜ƒrP<ĮlB‘l&w­ö)nMņN°³ŗp^Ž2_:]Ā'ŹyųD„>MĮšJ˜ R†Æ‘Ø%µ¦j€ź oĄó ~—ųа&ØPé“5Å}U'~õn?†} /ņü}B_xĪūā*EĆfć34V„6`wٽ6ÆĶMP$E&3sy3”ǹœnČåžåBiuu¹¬³:Ö×ÕŃ{°ŖK·3{œC—1³…ćOm{ŲŠņDČ {6ģkŃņ³ThĄÓˆįn6/Oü-ݚĪŗŽMŃėō ĀnŹÄ–jœ”2:Eµ ēÜQW‹ØDĄę“ŁU¬čLIĘ"„ł÷64ūŻqCO¹Ē„ēB\X“„2īt¢Ģf°kœÖ7.‰¾[}Iq‘E+ÅöTT–ŹÖElIƒŸļ¹Š«ķNS’jĀ€Ė3÷mJOcŠ˜š$-„ ć0‰7SÓFÆĄ’&~tś­ē¢3f Č®r2ó endstream endobj 10 0 obj <> endobj 23 0 obj <>stream xœWiXS×ŗŽ1&{· *éVؚ uĄŖ­­Ö”užEEf3† !dXaž  A@‹uhj­uąxzŚžV=VĻŠz“ē~›.žūܬ½ēĒ=÷¹Ļ}ųAŲYģµÖ÷¾ßū¾š8āńx"c„Š%›d1”ö?ßęfšø™øY|„ƒY2ŗCĄ‰)äČGŽf²uΜÓThž ņ)ŌDo厄ī÷ū,\“hńfY|Ŗ<2<"Ńķż„ļ-w Nuūõ·-REdxœŪ|ņ!Y#‹•Ę%zFĘ')ÜöĖbƒāÜģ›’ėƒ’~Õ’ļåE-Ūwx“ģČęų-[åŠķ‰I;“ƒ”Į»Cöõ”ī ó Ųé}ą`Lģņn«>\³n‘)oÉ;5‡ŚK­¦Ž¢¼Ø©¹Ō>j5Ÿņ¦PīŌAźõ6åCm¢ŽP›©%Ōźźµ•z—ŚF-„¶SļQ;؝Ō2ʃZN}@­ öPžKM§x”+5ƒšEI(G*–Š£œØÉŌrRxj"N}ĆKåNŲ0”—?ß8Q8Q1ńsĮĮB'”\ŲA;Šu̦–yņŚŹ×š_ūóėAÆ9Ģp;Üv\ēųlRš¤œ>rŗ;Y2Ł8łĀŁ”ž©K¦.:8õgē7œßwöqž§(ST%€|§Ń0d­gømŽčV€2ŖĀT¬ą\dB,;']¢I?–å*‡½· ©5¢Źbkż'ÕŻ.$l[S’P’:+kTӍŒīÓczČ`Ÿ‚īÖ œø‹ČĀM»mq¾ƒdŗč0×aeĆņŌÕ¦n čļ]Z½×Oę&ĮŽ ŗ/»$ĖČąŚØĶ Š2 ȱŅįj3źdĄB£üāņ²2Ft澎ZŃ0£½&Ž[‚si£T„'‹5ōųÅF§[m]Åā|¹šznI.ŠGÉ)¾š“™"źmxŪEm1¢ TWQŅ‘G>˜ŖLŒ ’(§;ō•™-žp÷¹ąĶ8!6L”>’CŖo£=‹RŹHźj[Fžu}ģņ ÆØP–&"+:~œ”A‡2H!HžŲčH“ŖņŲģ.žCpl¬½Wģź4ś.:ĶżrŹł,‡„°‹aĆtŃļĢÓ`Dkaāś•Ż-’ˆS‘Õ[«Ń3`Śz{~’ęówÆą5b<,ŽŌ×Ŗņ1cJŚą£RoÉaĄ·ŁSBѓēā oł0ąxMŸ„3M쨪ģ‹ž}y:pɜ(ž¬—8qśōnm Ļ ą=XĄ‡.–Åo,œƒgā·žĪ˜ö—ēą³=ǬÄĆ^­=€WįwR|öy„|Lžió‰”aÉo%†XĮŻkÆs¼\ųE®Y‹āPŠ::!óńģ*}”1ĻP蚮 «@L}uIŲfŲšC9ŻfhTZ"ažĪ/  22?Ź&ÕūÉFTœYfa`7 Žp½ŗ”¶¼­/L98śZ+ļ\å‚Ų%°V.ōü Äßć?„ļÓ½!·nvŸś¦G,/O‘¦„'#WYFM·>yJ x1żĘa;8Ī7^Éō9®­— ĢÓT{Ć~ ś¼¾Bc `p•’‡˜`•åc ü€½t¶8ĖŽK!½ģ‚Ļ·Cķę WÅtoµµąF1#:<\L¬ĄõWŲqB»± „Źx‘ᔓéb½łNßērśK}Ż+ NUmŹ%r𜶫–…Ūnįq©änx‘BŲkØČF ¤Ń§k•›±‡ĖŠZŒØŲU—ŪņņQ±©ÄÄXq¾‚nŠ鋲ņQ’s8āó.BXιÕö•”“仞śīÓĪ6˜N Ƴ`ÖtŃ-.ö:›™©×ė¹Č5=»¤FUōw뇰¬?¼!ČŖčén¶¶UåVŖĶā¬2}*bźšĢbѓOmńž’M4v?Ŗ KˆM‰DŒg撍+ż– —Äå‡ė•ēQ/j1wõ}Ē>WŲų˜1±ÉMŻWĻžżc]¦Ē›…Ē)­lZavAv9v„Ė3p®m°”·ŗZń=˜›oD©H•y\“£RŖ’“›Õ'tEɧ5õØUVµżZƒĘÜ"%ŽĄG\āĀ⣲Ņe”²„&+'CŸj ]“ÖJO.FÕČÜŲÓ՟Ÿ_TR\zBV•\–^äR¦*Q"-ŅØs¢ķ‹sĘsš›÷ ģ²Ų9+‰“ŌŒŗ±ē“„Y¦`fģEżÕåž Õä ®OžĆŪ Y÷Ģ?‘"•@>DĆ!|‹=Ń[knEĢpĖNģ‚%±»vÅ· ¾Ō„Q…÷³Ż\œ!…F­M‰‰ˆóN]‹O»žp1tŽżŖŹļģÄł‡œžœh—iœ8FŸōŒ-Z¢]#äœ^é79W»ŻĶęsKĒ†%¤#fł0ółékCęjmF‘¤0 !d:R“Ō…˜³Ö3Ć;—į™ fć¹Xņh!L½¹åŽU‰Ó/[^’ųē³\3¼Ė’e‰]_Jœź.ĆyŅy÷ŖŖīÓŅŲw,ūr1\$ŽÉ­IGäd2Śč©Ö¬³;œŚFXØ“ļ1\ …cMB(äž)+¢ ū՚Dzį;ś•fņøU°œ• µ±Y©i‰~ŠĢŒLœŅø‚Ę1šĪĘsBŪ_ĶąfÄź1w9'ér…›}™ĄDčķN`"}žkéeżóÓ+ŃY†»ƒ£ō ]Y6q¼‹ģ5ūĮ™°Ś Ļó~æšē‹­ę ŗæŅV8Lzy÷p‰@ԊžĄ¢œĢćuJbrFbÉ?…7€mśōjk‹BZe÷®T‹Į^+ē}Üę>’ūHĘI± ח¤Öļ0Ü|šHŒ÷X ™ū^ˆ®TVŻ(%•‹–Ów³-Éh ƒÆŅųÜF— -ØF5øŸ‹£^ij­Ö;°·–·šøł0• >›ˆBQ܉äöŌÓŚV1ęBaΟTŃ]Ńg|-ŽäNSVŗćłxž7‹`Ź×ē[žŽąjXĻęƒć‰īóhUGęa>£¢Sóv¢ę’A‚7kt;‹Ć°;^€„$#,Ą ˆū’ßä'I_.Āžc ®ćI8{bf0½x1׀×$D¢ ”³ūąX ļ6Qiāå-Vį¶č[Nō„ŖśŽåŁš\}NN¦8K„ĖDLL£¢­Ófėüt_ϊ>Š€xqr„6}Ąģø‚æ24ž:R Ōäµó~"aįCŲĄ'å~`®-Ֆī1ąIĆ S[A_Ķ×-}Ÿ”?0Ą,ž$Ę#öŠV„ŹŪ̌yŅĘ=éIž:=Ź4d0r`j*Zæm ĄŌn4!Q¬H‰ŹŽ‚Č^z|š»u1«Å¹łį˜ERćŒ°Æ¦‹vpÉÜö"žF‹NšdE!3°ĆŹ‹#*¢­2I½¼V= \Š£ą©×Ń™ŻŚØ„ą7÷D‚3|ō`čĒūūzgē‰=L+LŖBŌģŚŅ\׿CŖn‹®|ÖxŖwųMŌ­9%kfD·×b?iL‹ō;ą¾ĢƔƮø¼¹A"ZüˆĖbj„3~ģXjūÓ§':%Æ»^Żziq|®ÅŹF˜ŅO ’ ĒēCa)ū|ÄŠ†(螬¢2†Œ’»1¤ ø¬¬l|AĢæB&“ń@HŁėł£66© 3«°t¹|Zk«hn<ŁdīFēīĆx×8wtUéyķp6fe®#:m4Q™rĆ7Ń‚?@pl÷7Tē•™ mˆł”3pĪ.Uht²$įxdöz#É-ćś1ļ:ļāłöž%V6²@]ŗnŠż®}Ś’¬W©*[ėŸI. „‰3½FˆäßÕ××֌ףh/%é7[”ē>Bšp˜.ZÅ)9 ±±“6>';•¤M~2‹ū5~ŃE”3šĢUė°hłŠnpŲ/¹ż0Ț€¹n;°ĪėØķ¼TߙŽzüK&£“¢ƒ3ń®r}}öłćŁ}$X»’ō8ųxœ–ąw?ģN²”A×į³]_œ;« ė·E˜ć,ŒŻ5^&Œ"{Ā(³±+ 2Ź ÷ĪJĆdݐ_ v`N ą'ū›Pż'…5ą0ŌS ˜1+mXŸ‘¹Ņ®»£vš”ēG…ŽßaŒōńG÷ż:ąÜĻ­KF›ĘÅÉŻ{v˜š)Ģƃq'śŚīDåō­œruYō€F ėŽž.`0āzź]t}YßŪy®«ń÷č whÓgUęk„/!²Ÿś¦=ŗd¢QšÅŲĶpš~Żj¬Qš¤ć,”¦&GĒ‘q½÷jŖ¾ż’Ė/#›ļč%++5‘ÉŗēÕd½?4q×aqģµėn“ łĒK÷2¢óćcöŁÜŠIŅö$m„# 4UčĢ8æ‹Ģ'*ĖŃ×ę:sń‰gŖ÷Śn U«Āķ W“Åł¤ŗĘšk+ĖźkČJÄtU+¼Ę—†ØUaöøEĒ˜|ī=‹3X`~ ޚ.zF¹„å¶~D{­ĆĪxÕöÅ{»b˜š\šŠt̽|2×āąiX*)\ 1RÆn‚’> endobj 24 0 obj <>stream xœxyXS×ŗžŽ!{o­µ nLĄ&v°Z­cµJµĪcE 2‹Š ó<BBHÉJĀH˜ēq¢āl«ÖZœ:P;÷“÷TN{ĻŚtŃūüV ēwīpžūĒ}ąkķµ¾õ}ļ÷~ļ·8„Ć ‚ĆįĢöŽŠ KYq8!.8Ž>^ĘŗrŲ…3Ų¹»‘į÷J+œM€Ł\0Ū”fį‹Ÿ8²~óąČ 0}.įĄįl8ąW¶Ōē°ßėĖ—æ±3!139*"2uŃŚÕkÖ- É\ōēĢ¢]a)Qń‹^Ƥ‡Å&$ƅŧŠŠ IKY4uī¢Ćai±ĮÉ’ł’Üķ’¶?Aļnß‘°3q×īä=)©ūŅö§g‹CÜ3O”„†½īy8Ź+Ś;Ę'Ö7ĪoŻ[‹^v{åķMļ,ٲ“óõ@ż†c+V­ZµFŗVö&A¼L¼G¼BxoÆžÄbā5Ā‹XBx>Äė„/±Œš#ü‰D±“XA!v+‰@b7±‡XMģ%ÖūˆżÄ›Ä»Ä:ā±žp'Ž"ˆCÄF‚!‚Kø„+±4!"fMÄ,b3G·ų¹śŁ3fgĻžōy÷9/Ī1½0÷…ȹĻĶUϽ7/hŽ=ĒŁŽ”NNNV§'ó÷ĻwŸ_4’?˜X¦Ńy†ó"ēētēsĪ.˜·`Ė‚Q>ĆäĆg|Įė‚m‚ZtyÓe‡K¹Ė/®Š8g"Ų ’)ö%gĀ׏7*K#G‘żŸA¢ŠIłōš<+P+Ć#V*0£¬„ĢXQX.‚3yŠBžZ^–Xœd,ÉõÕŃbXBMfĆRR°ŅØ™7‡Ķ6–į|:wq«a¤… 0f•ƒnvRēĪŌ–uś|}ģ~ £“ļŹ³ŽäÓRj”Ž*Źtēi˜Iżtyū±XɁ½Ā'”Źą'ŽRųIŖŲhŅ›Żl’!=„óPdäÓs&VvÜęyćP3ž6¤8żP5±€Ә•śmō¤„Ū–-wĖĒĘ~E=÷kz:»ŪmgĄ90(éi‰kJ,{·rø¼¦“¾†vzÖPké>ķÖ\EŪ„Øƒ„GŠ7ĢĻ`HŻ-„~ø’ŚrŠų¤‡žLÉ ÉŻč#2S»ö;@ „Æ-(ŖŅźłNĻ ×ų”’Z°Ł?x¹ŽĘNQ6³k›9-ߊo¹ŠČž`Š‚5Ė‘-üa t„Ž?żEæźG4_”ĄŒ][Ž^Däń}ŪBĆėūÓE ĆY÷ĄĒō_†nŽNEpb†ļŽs'¼&–1(ALėŖrA PęɕRō J磄0YQ©®f°V”“ @)0ėi+źS瓲_ŠKpņ棔ī͜œÕyŲ?׬ŌZC¶Y÷ˆ†¾\ Óo|ŻX{±@€},d’ÖĻĮ1ęN¬“Óxé¤:=Kžr€Ō(- įŹIŻ]§<ķ Ÿū:ĄÅpīīo–y„x‰QjƒOzd¶·DE™ōEųötg±ņØh2’¾ „‡šv:W²mLėåī”Ńó[ŃL”“?šqdŪĪ£‡»[§ÜÜĆ~ŁĆłuœ%!‡[é {HHƒß:n^žšZĻ7ągšmücƑ½!N7zŠØNL>Q•äž ¹rł6 ³0m„%§æś +źœš±Ļ©õĶ ŁšöQ’xOł:@Ÿ”cĶa!ĪĖė§ ©—ó×q.ū#|‡I _:žŒ^Ńщä+FōŃĄ•Ō˜¶\^īF×Ai*)[&ß „ŃJ˜Lź[ š ]±Z›¬r-Ż@ęĆWx“ĶbņMY¶a§Ż2ķĪlŁNl¶‡šŸā\Żhć<ć:C•…ÜgČŖŠ^£aÕ~śBs‘I£4 -Ņ2M9 ė-UĶé¶Øą„“¢ļ)Į+-BꕉŻ[³ Š ¦¬ 2RZ?µŚ]Cųž¹R Ģb°É”b³Dę†M^×UŹŹŽĘ ”µr»¾ĪńāMčvoģ]x‡Éƒ/ņ¤&W“Æj 4( SKR S½ł°ßNÆö“_Š®FweT„€XAPL‚’žČŽńTaZ]N—šœüĶ L[g‡Šŗ©1ĻęśųÖÕ{R:Žė!āƒ„*i=hœjkŗŅ½¦N8¶)‡ŁŁķœ{c\XĀś2ÆAš,Õ5BĻ qQń…Jm·$_Æ5jé÷Ń%rCė‰kē{ė‡;„Łę“¤¬œ4 —6ŻĮ¾gŌ4SŸ„Ż6ĪßĒįåq®Õ¶YÉmEņ2Ż}†ĄĖČ»&GńŽŻ5mVźĻփŅRZśØēz›˜z¤.Uź·ÓČ]ĘIż/f¦<µ‹¦®²Ž6ĪĒcpĻŽŲFC1ØJtgiųZ+”nę—)AäHŠČdŠÄ“Xy Cc:O‹ąōšŒ:‹©EH£Bjó9’Ozś*›š„¼Tö‚­Ū|±B0…ŗ)£Ęķõį‚•t3Č*“£vĖ ŹĖ¾0bū.ˆ©/’Ią[åŠ łvūd6v³Ć&aē"‘”lĻ·((µŹ<åRTĘ_ MźŠü `€jsq—‘¶ )Õ«-V½ g£~¾AmTsńŹØ1äh —[zą|8Źo¹[TÜjÄ%üĻcīāc¤T§ŖT$@ŖNQ$®AÉü 0U2(€ZsńŁb|H”źÉ+“Ū| e¦ä"ÉjČ_ +ņJµ„ TjĢ%]xÕ>)U§-QÖśCWō~YlIv(%eeM˜¼TüļĀfŠA1(4UöC!ü+æś“±Š† ę°Ź+‡Æ²üzĒĖ_Ą„Ÿa”³‡n3™\#tŖŖbPż(0¬i‘Ö§uŸ°zzÅö#ī©ÖŒśś[m®PW,Қt% ˜njÆķ?ßļ#——E鍠Šż¦œ`Ńd%;żäĆš8wėKyFd}śĪv77(Å5ĀöŲŚ½`'øÜüŽ×6 ķ¼ŻĒžb³3»įˆFDˆ( (a/ŲD#ęÉJČ=Ó_ŁÖ-lŖ³6·^£ēüžüŸ Ļd«±hłÕī§@%MPką”#bņó<³Ā€“}„})[āf§4¹•ŚR ­Ńžf—P°`ņ,Ƃ„&öo23–B±łĻ’°³ »B;Å{†ūeåŸbOz€VÖ„•þ]™Ģ‚Ģ2łņ|’°–}gAæPłhƚ,łøĀrÓ@C!‰¬“Æó¤ģó”4ƬK3§é)ĢĄ(æŹdėŌ £=Ē<$b?Ą…ļhV”bæ]W”č  Š=&eˆ­¦ĀŚĶ'± V"zĶ]¹—{mg;E؂|ød˜‡–_g€J%W*$IŹx@ošłĪ…ēG^:wŌWdĮG0¾RŻö ø‘ Üz"ž8 N“Ä_ÉģUµkoŅš™ū”²%¹=®ūXG3âކ$īn4Ž_©æ<ø‰ÓBō¾Į˜wöÜ7@}Błŗ)fėf’£›Ó Iv $¹š‰ ņB ±f<¾¢}Õ„Ć¢«ī߄~UD\ˆ%éńæĘ?õ¾&r’hC÷r@G8 ”Ģī˜KGŚVōšļI÷ĖI–$§¦FFĻōĒĄńźˆ6’”čgrĮxóϧĻŅ}ēkG=-6^n‡Į͜æįśxӎńp¼fmķ­-WĘW +“Ź”UXiTW×u5¾ēī—xNŅ^ ‘dž|ų“pš¼/q>ƒ‡0ī³,ŒIS¬ū˜†Ōo!—Ü<‚SŽ;&„W(1Ržœė%H“ShŽ€K(=P’{B4ƒ O“{X…k”< sĶZü½š:=ŌZՏ»¾†ˆŻ"t„ŅīŹQÖąE‘”½{³±ć=œńq˜£6±ÄŹ„3Jć;Q,āC§†KĄ÷` »'©%ģԆƕ8*-br4·ō’6G¹Mۊ٢ ¬ ØĖ˜ƒnšązr2}Ź4›Kśį¼«'V¤Gˆ…Ē’"ÕĖq}€mųģ«ćŽĒß[ąō–ųJ ¹æPe²o©<#ZåĶ“ā,/3”éK ~JąJ)ķöåŽ|ŚéKč0±šILMMˆÆMkn­­miI­ĒIü¾¬;ĶĘ¢SQ6ĒŸBٳNo°^8ÓŅI§…iy ¹ÄU $’Ś©£"<¼$Ś5I¢ć#c3ƒŲ4č<ī‡Ÿl3e¦ƒdzĄ±kŽĄ9ĀT²EņjI'w«¾¬ĀājUyVŽIŚ{*·ĻŅ_?łõsļ’¾čå+Įƒ`„9ŻsóĢiqD—°-¦"©Ņkl|uß Ż§®’żŌõ€>ģ1Fc<˜–ķe'·B½Å`±_»Ī$Į±}[ó“j„,W†ė‹²XR“Y'3į’’š)I‹éKģÆhhu·×ŽOp+ķ?±õ_97:ģϧ9°÷žVĘ­ » g#[r‰Ā=@¤qčzR ·š&oj“…WOÖANM¤ ō„É|R·>G¹ĮĪügØiĢ®“q>ć;Ćl ™”6ŹĶŗt§ą"ō^ĘØ1ę J”F5ŠŠ99j¹P,…ń2Čk ¾ˆV@_~Kœš*?ŠbqrBFõéĢy ääd˲q>‡Ļn¤ņ ”ņųœĆRAœóåz“ŽŽNš²E“é”Ī7?—bŠ\Yֆ;FųĒ wĀĒ^å¤bņ‘Ę’ax÷‡Ų«“Ä%ˆŻv#õ‰g3ŪU=ą ?™꟦…y·˜zņ§d’BŪ?sƒ[ ēAׯ>ażiÓĄ­yeųĶ\ń™iĄœćÖ8C……t7*Ju—iöGl®VøbŽbe֗é±Ęi/•įŽ7‰ŅłČdóģĘ*lĻŪ8ķf‹É‘¼j%HYŖDyZ2Éå£åš»čŃÜZ`[…©ŻPĢśrū ‚ULĒ2`-ŌMń-$LfÆv<1›Ļa 1aÅ!įsžŽAwūƽqN¶žś,³®Ÿ†]0””7Ō Tuz°63B„=¬TxŁy=ÕByY„w÷…Tė@{õY@_©NÜ(B”ÖC­ņTćE *ÉSذ€> ”ą¬Šė[<ż“<}„™W¢š|Ąq˜½ńż€R|ÅQ _ūćQŁōćQ£)+P„Š(­W¶Ģ„ßwM»į÷v7`rQS­‰ CØL@óŠ÷|ć?ŖŖóK“‚œµ˜A}]é%»lbꊮ4§sS‡źBƲ³UÕyS/.µE š­˜ŗØ±dö¬€>“vO­d7Įu“›xņ)“×@.|ß:l6½_:­žį’ŲwlŽšĢć-pJ²±øg^Ģz‘čΤ'ļģD›(§gą¦qķū€A*ŖąOöG7Y3ė0Äéü|ĖeOlc©ōļšGÄDäÖ5½9Št)å&®īsŸž·ĢĪOŃĢ  U˜»Ø .ĒM×4Ņ(õ1_ß‰Æ?#ōŠī7}~„«.–4Ū,¢šŹ6S—½”ē°oL¼ŠŻ0»“1VŹ ¦Ź‡¦ė …®W§¦ˆ3S%kŖ— ŃRē’e› •¬C3§õ{īµ4:”Xļž’õį 8@Wŗ­†3‘3znķkČ͹’6$FNWžz_‚6 ī"ōF ÆĮ däå(rSĆd€Žų>w„ņzM£ØŹÖ`nōWg֣͢i’ś@—ų.ÖŲ1°žYBŚ  ļ·‘ed+<Īó$ćŠqmSƒXū`–7œA¦#¢½’NF£XŽU²Ęņ  ž·Śģ‰„ГɰÆs!kšž858øzČå|¹\ø bā’“cā’[Śė[Ū’ćD9ü’įÕ¶¬_”rń—ņ¾NūžŽ^b>źø®Ó#{G¤¦E ņ£kÅ=ÅÅz}‘Š)ŅPØzג"unLBøOøØ—}Ģ8%žsó†–éĶŁpömčr›Ć¶±Ī̤~?«'‘‹ĆōX”±żŹ{ūø’Īīfītœ¹O¼Łw<2%5*¦VÜ[\d?®Š€ŠöÓ¢"šixŪ‡v5¬āS“«a3ļU ^CĮč ĞxjGʹ§\x—ĄķGā’ƏķZ¹ ™ĶļYöõāSiĆ1e•üM·ŗGSī(€gą·ņ/šn4ßlķ¾•¶žh·&2·±µŌŸ»ĀĄ§Ü^“›=ߌŠ3ōńW]æ8Ąłį?æ9r.©ÖÆ]žÅ’Ō½)²y[ż>ó6šx9Ū-ńPāĮŲšžvIŠV7L˜8_?äĀį‰yLŠųćŒhXOEéeܞM¬£ōķfs‡~ś9‹}©†6pž:ß㶱뙻č!©ŻÆ”{ė0/%bņ½.¹šó§ŌĻĄØÕū`IéöØsjž±@Y®ū† ”‹ŽŖęēOŖbņC€­ u=}…•åż­g@3ؖ7DZĖķŖøĘRY7Š“e«gŅ‘Q\ˆ"\½‰Žƒƒf“ćÅ®ĘH9ńFŹ‹›Š©&ęŸH9ńߑ2}-,Ė]FµMŽĻĘ ÖŸµ³o1”Ū¤v‹JłŽ?x¹PVfśz Æ(]€4Ėß>c”| ”&š>ĶžŒ{£­2_®8½‘#–jr]5@i”ŅNGĶ ńEÉ®ĒĄ±”„ŠøŹŠ¼7é]$üŃyŗaݳŗ÷ ”åV'ŅN± Öņœ:×`Ōż§(Įwr;Ń&FL\CÉO©N]…TA’±ŽŅÅ(Ń:ūbč{‹ÓuÜ>XŹøCŻ-Œ#ĪEĪ={Īū妔]Ėó/pŲ<Äg.LŖšm2Ū„Ć«#v‰2’ŲĆ»KŚY¶q±‘S’,’‰ ‹ń¤7Ī šō CŌžgt ś¬WOŠÅ”«ą"Øźīŗ:Ņ iŸ§ašjųZ ĢObĘĪą1 …-Zæ>čļ0F3žbCR'–āSŽrįߊfoeZ›ī! 7Sŗ/Å Ū«G­ÖFż˜}¬XŪv·’6Sh.Ö¾D£=”nwfņžĢŪāźdżnķ¦Œ‹z£1N–‰¹xß(œƒ³œa‹…Ü$fŻ9{Är hßjV=–śb܏֘¤1"Ü_kŻ%²­vw½s›{˜ŪæKöŪ=²u£ųŻS.›_d² Ć=Ŗ£:Ź%įføė›ś‚Ē‚_Ž~ōŠ·_ŚÉpaL”4F²«FĆļ’·®ö{€žņņį ›­Ś°V„ö Ožœu”žqķ$|mē‰5Ģ¬ŗXżVįk¾eMĶÜ.'é6ŲĒŗ­©±ū²h…ļÕ×aųwī”ō÷Ŗn[÷Wg6čīŃp7•‰°v4}oÜģ~wŻ~ü$ģ>Ö÷pü‚¹ź“u9·R{½Ąś-ÆŪ2ŠėƅńՒb V²RYzä@ڽ‡OO=ÕpÜĆYC méµā²V¬łļžmč¹äņ¹ūČŹ€ IL„0.Ašœžž%ß?z¦ż ož8!LNÅ᩽)ōYė[ W½³¾x®×8{ö“‚ŁĻÄ’š!o9 endstream endobj 28 0 obj <>stream 2022-12-03T11:16:57+01:00 2022-12-03T11:16:57+01:00 groff version 1.22.4 Untitled endstream endobj 2 0 obj <>endobj xref 0 29 0000000000 65535 f 0000007062 00000 n 0000025780 00000 n 0000006989 00000 n 0000006559 00000 n 0000000182 00000 n 0000002024 00000 n 0000008286 00000 n 0000017198 00000 n 0000007730 00000 n 0000011917 00000 n 0000007266 00000 n 0000008976 00000 n 0000007127 00000 n 0000006701 00000 n 0000002044 00000 n 0000004389 00000 n 0000007177 00000 n 0000006845 00000 n 0000004410 00000 n 0000006538 00000 n 0000007227 00000 n 0000009265 00000 n 0000012287 00000 n 0000017722 00000 n 0000007645 00000 n 0000008198 00000 n 0000008864 00000 n 0000024519 00000 n trailer << /Size 29 /Root 1 0 R /Info 2 0 R /ID [] >> startxref 25936 %%EOF mrc-1.3.16/doc/mrc.1000066400000000000000000000151231501502116100137650ustar00rootroot00000000000000.TH mrc 1 "2022-12-03" "version 1.3.6" "User Commands" .if n .ad l .nh .SH NAME mrc \- A resource compiler .SH SYNOPSIS mrc [\fB-o\fR|\fB--output\fR outputfile] [\fB--root\fR arg] [\fB--resource-prefix\fR arg] [\fB--elf-machine\fR arg] [\fB--elf-class\fR arg] [\fB--elf-data\fR arg] [\fB--elf-abi\fR arg] [\fB--elf-flags\fR arg] file1 [file2...] .sp mrc [\fB-h\fR|\fB--help\fR] .sp mrc [\fB-v\fR|\fB--version\fR] .sp mrc [\fB--header\fR] > mrsrc.h .sp #include "mrsrc.h" void foo() { mrsrc::rsrc data("/alerts/text.txt"); if (data) { mrsrc::istream is(data); ... } } .SH DESCRIPTION Many applications come with supplementary data. This data is usually stored on disk as regular files. The disadvantage of this procedure is that an application cannot simply be copied to another location or computer and expected to function properly. .sp Resources are a way to overcome this problem by including all data inside the executable file. The mrc resource compiler can create object files containing both the data and an index. This data can then be accessed from within an application using .BR C++ classes. .SH OPTIONS .TP [\fB-o\fR|\fB--output\fR] file Specify the output file, the resulting file will be an object file you can link together with the rest of your object files into an executable file. .TP \fB--root\fR The resources are accessed using a path. You can specify the root part of the path using this parameter. .TP \fB--resource-prefix\fR name Use this option to specify another name for the global variables in the data section. .TP \fB--elf-machine\fR arg By default mrc assumes you want to create a resource file for the machine it runs on. But using this option you can create files for other architectures, useful when cross compiling. .sp The machine flag is used to specify the value of the \fIe_machine\fR field in the ELF header. .TP \fB--elf-class\fR number The ELF class to use, should be either \fI1\fR for 32-bit objects or \fI2\fR for 64-bit objects. .TP \fB--elf-data\fR number The ELF data endianness to use, should be either \fI1\fR for little-endian (=LSB) objects or \fI2\fR for big-endian (=MSB) objects. .TP \fB--elf-abi\fR number The ELF OS ABI flag to use, the exact value for this flag should be looked up in \fIelf.h\fR. Default is to use the value for the current architecture. (Value of 3 is for Linux, 9 is for FreeBSD). .TP \fB--elf-flags\fR number A value to store in the \fIe_flags\fR field of the ELF header. This can contain the EABI version for ARM e.g. .TP \fB--coff\fR type When this option is specified, a COFF file is created for use on Windows. The argument \fItype\fR should be one of \fBx64\fR, \fBx86\fR or \fBarm64\fR. .TP \fB--header\fR This option will print a \fBmrsrc.h\fR file to \fBstdout\fR which you can write to disk and use to access resources. Use with the \fB--output\fR option to write to a file instead. .TP [\fB-v\fR|\fB--verbose\fR] Print debug output, useful to track where all data ends up in the resource. .TP \fB--version\fR Print version number and exit. .TP [\fB-h\fR|\fB--help\fR] Print simple help summary and exit. .TP file [file...] One or more files to include in the resource file. Directory names can be used as well. All regular files end up in the root of the resource tree, files found in directories end up in directies in the resource tree. The following rules apply: .sp Regular files are added in the root of the resource tree using their proper file name. .sp If the file name refers to a directory, the directory is traversed recursively and all files are added. If the file name ends with a forward slash (/) files are added to the root. If the file does not end with a slash, the name of the directory will be placed in the root and all contained files will be placed beneath this. .sp .SH EXAMPLES .PP Here's a list of usage cases. .TP mrc -o x.o my-resource.txt my-image.png Will create a resource file containing two resources accessible using the path "/my-resource.txt" and "/my-image.png" respectively. .TP mrc -o x.o img/my-image.png Will create a resource file containing a single resource accessible using the path "/my-image.png". .TP mrc -o x.o img/ Assuming there are two images in the directory img called my-image-1.png and my-image-2.png, the resource file will contain them both accessible under the name "/my-image-1.png" and "/my-image-1.png". .sp mrc -o x.o img Same as the previous, but note there's no trailing slash, the resource file will contain both images but they are now accessible under the name "/img/my-image-1.png" and "/img/my-image-1.png". .PP Use the verbose flag (\fB--verbose\fR) to track what ends up where. .SH DETAILS .sp The way this works is that mrc first collects all data from the files specified, including the files found in specified directories. An simple index is created to allow hierarchical access to the data. The data is then flattened into three data structures and these are written to the \fB.data\fR section of the object file. The three data blobs are then made available as globals in your application with the names \fBgResourceIndex\fR, \fBgResourceName\fR and \fBgResourceData\fR. You can specify the prefix part of this variable with the -fB--resource-prefix\fR option. .sp The index entries have the following format: struct rsrc_imp { unsigned int m_next; // index of the next sibling entry unsigned int m_child; // index of the first child entry unsigned int m_name; // offset of the name for this entry unsigned int m_size; // data size for this entry unsigned int m_data; // offset of the data for this entry }; .sp The classes in the \fBmrsrc.h\fR file are contained in the namespace \fBmrsrc\fR. The available classes are .TP \fBmrsrc::rsrc\fR This is the basic class to access data. It has a constructor that takes a path to a resource. Data can be accessed using the \fBdata\fR method and the size of the data is available via the \fBsize\fR method. If the resource was not found, \fBdata\fR will return \fBnullptr\fR and \fBsize\fR will return zero. You can also use \fBoperator bool\fR to check for valid data. .TP \fBmrsrc::streambuf\fR This class is derived from \fBstd::streambuf\fR. It can take both a \fBmrsrc::rsrc\fR or a path as constructor parameter. .sp .TP \fBmrsrc::istream\fR This class is derived from \fBstd::istream\fR. It can take both a \fBmrsrc::rsrc\fR or a path as constructor parameter. .SH BUGS This application can only generate ELF formatted object files on machines that have an \fB\fR header file installed. .sp Only a single resource entry can be generated and there's no way to merge or manipulate resource files yet. mrc-1.3.16/examples/000077500000000000000000000000001501502116100141715ustar00rootroot00000000000000mrc-1.3.16/examples/esp-idf-rsrc/000077500000000000000000000000001501502116100164675ustar00rootroot00000000000000mrc-1.3.16/examples/esp-idf-rsrc/CMakeLists.txt000066400000000000000000000003541501502116100212310ustar00rootroot00000000000000# The following lines of boilerplate have to be in your project's # CMakeLists in this exact order for cmake to work correctly cmake_minimum_required(VERSION 3.16) include($ENV{IDF_PATH}/tools/cmake/project.cmake) project(my-rsrc-app) mrc-1.3.16/examples/esp-idf-rsrc/README.md000066400000000000000000000074531501502116100177570ustar00rootroot00000000000000Using resources in ESP-IDF ========================== It is very easy to add resources to esp-idf projects as is shown in this example. Here we create a single resource called `hello.txt` and its content comes from the file `hello.txt`. The header file is created and resources are added to the main component using the cmake snippet: ```cmake # locate and include the mrc macro's find_package(mrc) # write a header file mrc_write_header(${CMAKE_CURRENT_SOURCE_DIR}/mrsrc.hpp) # Add one resource to the executable mrc_target_resources(${COMPONENT_LIB} CREATE_ELF_TEMPLATE RESOURCES ${CMAKE_CURRENT_SOURCE_DIR}/hello.txt) ``` Now you can simply use that resource: ```c++ #include "esp_log.h" #include "mrsrc.hpp" const char TAG[] = "esp-rsrc"; extern "C" void app_main(void) { mrsrc::rsrc data("hello.txt"); if (data) { std::string s(data.data(), data.size()); ESP_LOGI(TAG, "Resource found: '%s'!", s.c_str()); } else ESP_LOGI(TAG, "The resource was not found"); } ``` Use `idf.py build flash monitor` and the output will be: ```console ESP-ROM:esp32h2-20221101 Build:Nov 1 2022 rst:0x1 (POWERON),boot:0xc (SPI_FAST_FLASH_BOOT) SPIWP:0xee mode:DIO, clock div:1 load:0x408460e0,len:0x17ac load:0x4083cfd0,len:0xe88 load:0x4083efd0,len:0x2c94 entry 0x4083cfda I (23) boot: ESP-IDF v5.3-dev-1353-gb3f7e2c8a4 2nd stage bootloader I (24) boot: compile time Feb 1 2024 15:04:08 I (25) boot: chip revision: v0.1 I (28) boot.esp32h2: SPI Speed : 64MHz I (33) boot.esp32h2: SPI Mode : DIO I (38) boot.esp32h2: SPI Flash Size : 2MB I (42) boot: Enabling RNG early entropy source... I (48) boot: Partition Table: I (51) boot: ## Label Usage Type ST Offset Length I (59) boot: 0 nvs WiFi data 01 02 00009000 00006000 I (66) boot: 1 phy_init RF data 01 01 0000f000 00001000 I (74) boot: 2 factory factory app 00 00 00010000 00100000 I (81) boot: End of partition table I (85) esp_image: segment 0: paddr=00010020 vaddr=42018020 size=0a0e8h ( 41192) map I (107) esp_image: segment 1: paddr=0001a110 vaddr=40800000 size=05f08h ( 24328) load I (116) esp_image: segment 2: paddr=00020020 vaddr=42000020 size=1734ch ( 95052) map I (146) esp_image: segment 3: paddr=00037374 vaddr=40805f08 size=03acch ( 15052) load I (152) esp_image: segment 4: paddr=0003ae48 vaddr=408099e0 size=011f4h ( 4596) load I (157) boot: Loaded app from partition at offset 0x10000 I (158) boot: Disabling RNG early entropy source... I (174) cpu_start: Unicore app W (183) clk: esp_perip_clk_init() has not been implemented yet I (190) cpu_start: Pro cpu start user code I (190) cpu_start: cpu freq: 96000000 Hz I (191) heap_init: Initializing. RAM available for dynamic allocation: I (195) heap_init: At 4080BDD0 len 000415B0 (261 KiB): RAM I (201) heap_init: At 4084D380 len 00002B60 (10 KiB): RAM I (209) spi_flash: detected chip: generic I (212) spi_flash: flash io: dio W (216) spi_flash: Detected size(4096k) larger than the size in the binary image header(2048k). Using the size in the binary image header. I (229) cpu_start: Application information: I (234) cpu_start: Project name: my-rsrc-app I (239) cpu_start: App version: v1.3.10-23-gaff8ce4-dirty I (246) cpu_start: Compile time: Feb 1 2024 15:04:01 I (252) cpu_start: ELF file SHA256: 30e7d4161... I (257) cpu_start: ESP-IDF: v5.3-dev-1353-gb3f7e2c8a4 I (264) cpu_start: Min chip rev: v0.0 I (269) cpu_start: Max chip rev: v0.99 I (273) cpu_start: Chip rev: v0.1 I (279) sleep: Configure to isolate all GPIO pins in sleep state I (285) sleep: Enable automatic switching of GPIO sleep configuration I (292) main_task: Started on CPU0 I (292) main_task: Calling app_main() I (292) esp-rsrc: Resource found: 'Hello, world! '! I (302) main_task: Returned from app_main() ```mrc-1.3.16/examples/esp-idf-rsrc/main/000077500000000000000000000000001501502116100174135ustar00rootroot00000000000000mrc-1.3.16/examples/esp-idf-rsrc/main/CMakeLists.txt000066400000000000000000000004171501502116100221550ustar00rootroot00000000000000find_package(mrc) idf_component_register(SRCS esp-rsrc-app.cpp INCLUDE_DIRS ".") mrc_write_header(${CMAKE_CURRENT_SOURCE_DIR}/mrsrc.hpp) mrc_target_resources(${COMPONENT_LIB} CREATE_ELF_TEMPLATE RESOURCES ${CMAKE_CURRENT_SOURCE_DIR}/hello.txt) mrc-1.3.16/examples/esp-idf-rsrc/main/esp-rsrc-app.cpp000066400000000000000000000032621501502116100224360ustar00rootroot00000000000000/*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2024 Maarten L. Hekkelman * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "esp_log.h" #include "mrsrc.hpp" const char TAG[] = "esp-rsrc"; extern "C" void app_main(void) { mrsrc::rsrc data("hello.txt"); if (data) { std::string s(data.data(), data.size()); ESP_LOGI(TAG, "Resource found: '%s'!", s.c_str()); } else ESP_LOGI(TAG, "The resource was not found"); } mrc-1.3.16/examples/esp-idf-rsrc/main/hello.txt000066400000000000000000000000161501502116100212540ustar00rootroot00000000000000Hello, world! mrc-1.3.16/examples/esp-idf-rsrc/main/mrsrc.hpp000066400000000000000000000230671501502116100212620ustar00rootroot00000000000000/*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2006-2023 Maarten L. Hekkelman * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #pragma once #include #include #include #include #include #include /* Resources are data sources for the application. They are retrieved by name. Basic usage: mrsrc::rsrc rsrc("dialogs/my-cool-dialog.glade"); if (rsrc) { GladeXML* glade = glade_xml_new_from_buffer(rsrc.data(), rsrc.size(), NULL, "japi"); ... } Alternative, to loop over all resources: mrsrc rsrc; // <- the root resource for (rsrc child: rsrc) std::cout << child.name() << std::endl; ------------------------------------------------- Stream interface: mrsrc::streambuf rb("data/my-text.txt"); std::istream is(&rb); std::string line; while (std::gettline(is, line)) std::cout << line << std::endl; */ namespace mrsrc { /// \brief Internal data structure as generated by mrc struct rsrc_imp { unsigned int m_next; unsigned int m_child; unsigned int m_name; unsigned int m_size; unsigned int m_data; }; } // namespace mrsrc // The following three variables are generated by mrc: extern "C" const mrsrc::rsrc_imp gResourceIndex[]; extern "C" const char gResourceData[]; extern "C" const char gResourceName[]; namespace mrsrc { /// \brief Class mrsrc::rsrc contains a pointer to the data in the /// resource, as well as offering an iterator interface to its /// children. class rsrc { public: rsrc() : m_impl(gResourceIndex) { } rsrc(const rsrc &other) : m_impl(other.m_impl) { } rsrc &operator=(const rsrc &other) { m_impl = other.m_impl; return *this; } rsrc(std::filesystem::path path); std::string name() const { return m_impl ? gResourceName + m_impl->m_name : ""; } const char *data() const { return m_impl ? gResourceData + m_impl->m_data : nullptr; } unsigned long size() const { return m_impl ? m_impl->m_size : 0; } explicit operator bool() const { return m_impl != nullptr and m_impl->m_size > 0; } template class iterator_t { public: using iterator_category = std::input_iterator_tag; using value_type = RSRC; using difference_type = std::ptrdiff_t; using pointer = value_type *; using reference = value_type &; iterator_t(const rsrc_imp *cur) : m_cur(cur) { } iterator_t(const iterator_t &i) : m_cur(i.m_cur) { } iterator_t &operator=(const iterator_t &i) { m_cur = i.m_cur; return *this; } reference operator*() { return m_cur; } pointer operator->() { return &m_cur; } iterator_t &operator++() { if (m_cur.m_impl->m_next) m_cur.m_impl = gResourceIndex + m_cur.m_impl->m_next; else m_cur.m_impl = nullptr; return *this; } iterator_t operator++(int) { auto tmp(*this); this->operator++(); return tmp; } bool operator==(const iterator_t &rhs) const { return m_cur.m_impl == rhs.m_cur.m_impl; } bool operator!=(const iterator_t &rhs) const { return m_cur.m_impl != rhs.m_cur.m_impl; } private: value_type m_cur; }; using iterator = iterator_t; iterator begin() const { const rsrc_imp *impl = nullptr; if (m_impl and m_impl->m_child) impl = gResourceIndex + m_impl->m_child; return iterator(impl); } iterator end() const { return iterator(nullptr); } private: rsrc(const rsrc_imp *imp) : m_impl(imp) { } const rsrc_imp *m_impl; }; inline rsrc::rsrc(std::filesystem::path p) { m_impl = gResourceIndex; // using std::filesytem::path would have been natural here of course... auto pb = p.begin(); auto pe = p.end(); while (m_impl != nullptr and pb != pe) { auto name = *pb++; const rsrc_imp *impl = nullptr; for (rsrc child : *this) { if (child.name() == name) { impl = child.m_impl; break; } } m_impl = impl; } if (pb != pe) // not found m_impl = nullptr; } // -------------------------------------------------------------------- template class basic_streambuf : public std::basic_streambuf { public: typedef CharT char_type; typedef Traits traits_type; typedef typename traits_type::int_type int_type; typedef typename traits_type::pos_type pos_type; typedef typename traits_type::off_type off_type; /// \brief constructor taking a \a path to the resource in memory basic_streambuf(const std::string &path) : m_rsrc(path) { init(); } /// \brief constructor taking a \a rsrc basic_streambuf(const rsrc &rsrc) : m_rsrc(rsrc) { init(); } basic_streambuf(const basic_streambuf &) = delete; basic_streambuf(basic_streambuf &&rhs) : basic_streambuf(rhs.m_rsrc) { } basic_streambuf &operator=(const basic_streambuf &) = delete; basic_streambuf &operator=(basic_streambuf &&rhs) { swap(rhs); return *this; } void swap(basic_streambuf &rhs) { std::swap(m_begin, rhs.m_begin); std::swap(m_end, rhs.m_end); std::swap(m_current, rhs.m_current); } /// \brief Analogous to is_open of an ifstream_buffer, return true if the resource is valid bool is_valid() const { return static_cast(m_rsrc); } private: void init() { if (m_rsrc) { m_begin = reinterpret_cast(m_rsrc.data()); m_end = reinterpret_cast(m_rsrc.data() + m_rsrc.size()); m_current = m_begin; } } int_type underflow() { if (m_current == m_end) return traits_type::eof(); return traits_type::to_int_type(*m_current); } int_type uflow() { if (m_current == m_end) return traits_type::eof(); return traits_type::to_int_type(*m_current++); } int_type pbackfail(int_type ch) { if (m_current == m_begin or (ch != traits_type::eof() and ch != m_current[-1])) return traits_type::eof(); return traits_type::to_int_type(*--m_current); } std::streamsize showmanyc() { assert(std::less_equal()(m_current, m_end)); return m_end - m_current; } pos_type seekoff(off_type off, std::ios_base::seekdir dir, std::ios_base::openmode /*which*/) { switch (dir) { case std::ios_base::beg: m_current = m_begin + off; break; case std::ios_base::end: m_current = m_end + off; break; case std::ios_base::cur: m_current += off; break; default: break; } if (m_current < m_begin) m_current = m_begin; if (m_current > m_end) m_current = m_end; return m_current - m_begin; } pos_type seekpos(pos_type pos, std::ios_base::openmode /*which*/) { m_current = m_begin + pos; if (m_current < m_begin) m_current = m_begin; if (m_current > m_end) m_current = m_end; return m_current - m_begin; } private: rsrc m_rsrc; const char_type *m_begin = nullptr; const char_type *m_end = nullptr; const char_type *m_current = nullptr; }; using streambuf = basic_streambuf>; // -------------------------------------------------------------------- // class mrsrc::istream template class basic_istream : public std::basic_istream { public: typedef CharT char_type; typedef Traits traits_type; typedef typename traits_type::int_type int_type; typedef typename traits_type::pos_type pos_type; typedef typename traits_type::off_type off_type; private: using __streambuf_type = basic_streambuf; using __istream_type = std::basic_istream; __streambuf_type m_buffer; public: basic_istream(const std::string &path) : basic_istream(rsrc(path)) { } basic_istream(const rsrc &resource) : __istream_type(&m_buffer) , m_buffer(resource) { if (resource) this->init(&m_buffer); else __istream_type::setstate(std::ios_base::badbit); } basic_istream(const basic_istream &) = delete; basic_istream(basic_istream &&rhs) : __istream_type(std::move(rhs)) , m_buffer(std::move(rhs.m_buffer)) { __istream_type::set_rdbuf(&m_buffer); } basic_istream &operator=(const basic_istream &) = delete; basic_istream &operator=(basic_istream &&rhs) { __istream_type::operator=(std::move(rhs)); m_buffer = std::move(rhs.m_buffer); return *this; } void swap(basic_istream &rhs) { __istream_type::swap(rhs); m_buffer.swap(rhs.m_buffer); } __streambuf_type *rdbuf() const { return const_cast<__streambuf_type *>(&m_buffer); } }; using istream = basic_istream>; } // namespace mrsrc mrc-1.3.16/examples/simple/000077500000000000000000000000001501502116100154625ustar00rootroot00000000000000mrc-1.3.16/examples/simple/CMakeLists.txt000066400000000000000000000014371501502116100202270ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.16) # set the project name project(mrc-user VERSION 1.0.0 LANGUAGES CXX) # Locate the mrc executable and load the mrc_* functions find_package(mrc) # resources depend on C++17 features, like std::filesystem set(CXX_EXTENSIONS OFF) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) # Write out an mrsrc.hpp file and make sure the compiler finds it mrc_write_header(${CMAKE_CURRENT_BINARY_DIR}/mrsrc.hpp) include_directories(${CMAKE_CURRENT_BINARY_DIR}) # The executable to create add_executable(mrc-user ${CMAKE_CURRENT_SOURCE_DIR}/mrc-user.cpp) # Add the hello.txt file as a resource, more files and/or directories # can be specified here mrc_target_resources(mrc-user CREATE_ELF_TEMPLATE VERBOSE RESOURCES ${CMAKE_CURRENT_SOURCE_DIR}/hello.txt) mrc-1.3.16/examples/simple/hello.txt000066400000000000000000000000151501502116100173220ustar00rootroot00000000000000Hello, world!mrc-1.3.16/examples/simple/mrc-user.cpp000066400000000000000000000007021501502116100177220ustar00rootroot00000000000000/* Example of the use of resources created with mrc in a C++ application */ #include // Include the generated header file #include "mrsrc.hpp" int main() { try { mrsrc::rsrc res("hello.txt"); if (not res) throw std::runtime_error("Resource not found"); std::cout.write(res.data(), res.size()); std::cout << std::endl; } catch(const std::exception& e) { std::cerr << e.what() << '\n'; exit(1); } return 0; } mrc-1.3.16/rsrc/000077500000000000000000000000001501502116100133245ustar00rootroot00000000000000mrc-1.3.16/rsrc/resource-1.txt000066400000000000000000000000621501502116100160500ustar00rootroot00000000000000This is the first line And this is the second linemrc-1.3.16/rsrc/resource-2.txt000066400000000000000000000001461501502116100160540ustar00rootroot00000000000000ž’This is the first line And this is the second linemrc-1.3.16/rsrc/subdir/000077500000000000000000000000001501502116100146145ustar00rootroot00000000000000mrc-1.3.16/rsrc/subdir/resource-3.txt000066400000000000000000000000211501502116100173350ustar00rootroot00000000000000Dit is resource 3mrc-1.3.16/rsrc/subdir/subsubdir/000077500000000000000000000000001501502116100166165ustar00rootroot00000000000000mrc-1.3.16/rsrc/subdir/subsubdir/resource-4.txt000066400000000000000000000000211501502116100213400ustar00rootroot00000000000000Dit is resource 4mrc-1.3.16/src/000077500000000000000000000000001501502116100131425ustar00rootroot00000000000000mrc-1.3.16/src/dummy.cpp000066400000000000000000000003171501502116100150020ustar00rootroot00000000000000// Dummy file for creating a first mrc-mini application #include "mrsrc.h" const mrsrc::rsrc_imp gResourceIndex[1] = {}; const char gResourceData[] = "\0\0\0\0"; const char gResourceName[] = "\0\0\0\0"; mrc-1.3.16/src/mrc-unit-test.cpp000066400000000000000000000051201501502116100163570ustar00rootroot00000000000000#define CATCH_CONFIG_MAIN #include #include "mrsrc.h" #include #include #include TEST_CASE("test_1", "test_1") { mrsrc::rsrc r1("resource-1.txt"); REQUIRE((bool)r1); REQUIRE(r1.data() != nullptr); REQUIRE(r1.size() == 50); int r = std::memcmp(r1.data(), R"(This is the first line And this is the second line)", r1.size()); REQUIRE(r == 0); } TEST_CASE("test_2", "test_2") { mrsrc::rsrc r2("resource-2.txt"); REQUIRE((bool)r2); REQUIRE(r2.data() != nullptr); REQUIRE(r2.size() == 102); /* const char16_t* t = u"\xfeffThis is the first line\ And this is the second line"; // t[0] = 0xfeff; int r = std::memcmp(r2.data(), (char*)t, r2.size()); REQUIRE(r == 0); */ } TEST_CASE("test_3", "test_3") { mrsrc::streambuf buf("resource-1.txt"); std::istream is(&buf); std::string line; REQUIRE((bool)std::getline(is, line)); REQUIRE(line == "This is the first line"); REQUIRE((bool)std::getline(is, line)); REQUIRE(line == "And this is the second line"); REQUIRE(not std::getline(is, line)); } TEST_CASE("test_4", "test_4") { mrsrc::istream is("resource-1.txt"); std::string line; REQUIRE((bool)std::getline(is, line)); REQUIRE(line == "This is the first line"); REQUIRE((bool)std::getline(is, line)); REQUIRE(line == "And this is the second line"); REQUIRE(not std::getline(is, line)); } TEST_CASE("test_10", "test_10") { mrsrc::rsrc r0(""); REQUIRE(std::distance(r0.begin(), r0.end()) == 3); std::set found; for (auto &r1 : r0) found.insert(r1.name()); std::set kTest{ "resource-1.txt", "resource-2.txt", "subdir" }; REQUIRE(found == kTest); if (found != kTest) { for (auto &f : found) std::cout << f << std::endl; } } TEST_CASE("test_11", "test_11") { mrsrc::rsrc r0("subdir/resource-3.txt"); REQUIRE((bool)r0); mrsrc::istream is(r0); std::string line; REQUIRE((bool)std::getline(is, line)); REQUIRE(line == "Dit is resource 3"); } TEST_CASE("test_12", "test_12") { mrsrc::rsrc r0("subdir/subsubdir/resource-4.txt"); REQUIRE((bool)r0); mrsrc::istream is(r0); std::string line; REQUIRE((bool)std::getline(is, line)); REQUIRE(line == "Dit is resource 4"); } TEST_CASE("test_13", "test_13") { mrsrc::istream ri("subdir/resource-3.txt"); REQUIRE((bool)ri); REQUIRE(ri.eof() == false); std::string line; REQUIRE((bool)std::getline(ri, line)); REQUIRE(line == "Dit is resource 3"); REQUIRE(ri.eof() == true); } TEST_CASE("test_14", "test_14") { mrsrc::istream ri("resource-5.txt"); REQUIRE((bool)ri == false); REQUIRE(ri.bad() == true); } mrc-1.3.16/src/mrc.cpp000066400000000000000000001015451501502116100144350ustar00rootroot00000000000000/*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2017-2021 Maarten L. Hekkelman * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ // // mrc, A simple resource compiler. // // Use this program to make data available to your program without // having to distribute this data in separate files. // // Example usage: mrc -o myrsrc.o rsrc/ // // This will create an object file called myrsrs.o containing the data for all file found in the rsrc/ directory. #include #include #include #if __has_include() # include #elif not defined(EM_NONE) # define EM_NONE 0 #endif #include #include #include #include #include "mrsrc.h" #include "revision.hpp" #ifndef PATH_MAX # define PATH_MAX 1024 #endif namespace fs = std::filesystem; int VERBOSE = 0; // -------------------------------------------------------------------- uint32_t AddNameToNameTable(std::string &ioNameTable, const std::string &inName) { uint32_t result = 0; const char *p = ioNameTable.c_str(); const char *e = p + ioNameTable.length(); while (p < e) { if (inName == p) { result = static_cast(p - ioNameTable.c_str()); break; } p += strlen(p) + 1; } if (p >= e) { result = static_cast(ioNameTable.length()); ioNameTable.append(inName); ioNameTable.append("\0", 1); } return result; } // -------------------------------------------------------------------- struct elf_config { int elf_machine; int elf_class; int elf_data; int elf_flags; int elf_abi; bool operator==(const elf_config &) const = default; }; // -------------------------------------------------------------------- struct MObjectFileImp { fs::path mFile; uint32_t mTextSize; uint32_t mDataSize; virtual ~MObjectFileImp() {} virtual void Write(std::ofstream &inFile) = 0; static MObjectFileImp *Create(elf_config elfc); protected: friend class MObjectFile; struct MGlobal { std::string name; std::string data; }; typedef std::vector MGlobals; MGlobals mGlobals; }; // -------------------------------------------------------------------- // byte swapping namespace Swap { struct no_swapper { template T operator()(T inValue) const { return inValue; } }; struct swapper { template T operator()(T inValue) const { return inValue; } }; template <> inline int16_t swapper::operator()(int16_t inValue) const { return static_cast( ((inValue & 0xFF00UL) >> 8) | ((inValue & 0x00FFUL) << 8)); } template <> inline uint16_t swapper::operator()(uint16_t inValue) const { return static_cast( ((inValue & 0xFF00UL) >> 8) | ((inValue & 0x00FFUL) << 8)); } template <> inline int32_t swapper::operator()(int32_t inValue) const { return static_cast( ((inValue & 0xFF000000UL) >> 24) | ((inValue & 0x00FF0000UL) >> 8) | ((inValue & 0x0000FF00UL) << 8) | ((inValue & 0x000000FFUL) << 24)); } template <> inline uint32_t swapper::operator()(uint32_t inValue) const { return static_cast( ((inValue & 0xFF000000UL) >> 24) | ((inValue & 0x00FF0000UL) >> 8) | ((inValue & 0x0000FF00UL) << 8) | ((inValue & 0x000000FFUL) << 24)); } template <> inline int64_t swapper::operator()(int64_t inValue) const { return static_cast( (((static_cast(inValue)) << 56) & 0xFF00000000000000ULL) | (((static_cast(inValue)) << 40) & 0x00FF000000000000ULL) | (((static_cast(inValue)) << 24) & 0x0000FF0000000000ULL) | (((static_cast(inValue)) << 8) & 0x000000FF00000000ULL) | (((static_cast(inValue)) >> 8) & 0x00000000FF000000ULL) | (((static_cast(inValue)) >> 24) & 0x0000000000FF0000ULL) | (((static_cast(inValue)) >> 40) & 0x000000000000FF00ULL) | (((static_cast(inValue)) >> 56) & 0x00000000000000FFULL)); } template <> inline uint64_t swapper::operator()(uint64_t inValue) const { return static_cast( ((((uint64_t)inValue) << 56) & 0xFF00000000000000ULL) | ((((uint64_t)inValue) << 40) & 0x00FF000000000000ULL) | ((((uint64_t)inValue) << 24) & 0x0000FF0000000000ULL) | ((((uint64_t)inValue) << 8) & 0x000000FF00000000ULL) | ((((uint64_t)inValue) >> 8) & 0x00000000FF000000ULL) | ((((uint64_t)inValue) >> 24) & 0x0000000000FF0000ULL) | ((((uint64_t)inValue) >> 40) & 0x000000000000FF00ULL) | ((((uint64_t)inValue) >> 56) & 0x00000000000000FFULL)); } #if defined(LITTLE_ENDIAN) typedef no_swapper lsb_swapper; typedef swapper msb_swapper; #elif defined(BIG_ENDIAN) typedef swapper lsb_swapper; typedef no_swapper msb_swapper; #else # error Undefined endianness #endif } // namespace Swap // -------------------------------------------------------------------- // Write data aligned to some alignment value uint32_t WriteDataAligned(std::ofstream &inStream, const void *inData, uint32_t inSize, uint32_t inAlignment = 1) { inStream.write(reinterpret_cast(inData), inSize); if (inAlignment > 1) { while ((inStream.tellp() % inAlignment) != 0) inStream.put('\0'); } return static_cast(inStream.tellp()); } // -------------------------------------------------------------------- // // Implementation for ELF #if __has_include() template struct ElfClass; template <> struct ElfClass { using Elf_Ehdr = Elf32_Ehdr; using Elf_Shdr = Elf32_Shdr; using Elf_Sym = Elf32_Sym; using Elf_Word = Elf64_Word; using Elf_Half = Elf64_Half; }; template <> struct ElfClass { using Elf_Ehdr = Elf64_Ehdr; using Elf_Shdr = Elf64_Shdr; using Elf_Sym = Elf64_Sym; using Elf_Word = Elf64_Word; using Elf_Half = Elf64_Half; }; template struct ElfData; template <> struct ElfData { using swapper = Swap::msb_swapper; }; template <> struct ElfData { using swapper = Swap::lsb_swapper; }; template struct MELFObjectFileImp : public MObjectFileImp { using swapper = typename ElfData::swapper; using Elf_Ehdr = typename ElfClass::Elf_Ehdr; using Elf_Shdr = typename ElfClass::Elf_Shdr; using Elf_Sym = typename ElfClass::Elf_Sym; using Elf_Word = typename ElfClass::Elf_Word; using Elf_Half = typename ElfClass::Elf_Half; virtual void Write(std::ofstream &inFile) override; MELFObjectFileImp(int machine, uint8_t elf_abi, int flags) : MObjectFileImp() , mMachine(machine) , mABI(elf_abi) , mFlags(flags) { } Elf_Half mMachine; uint8_t mABI; Elf_Word mFlags; }; enum { kNullSection, kTextSection, kDataSection, kBssSection, kShStrtabSection, kSymtabSection, kStrtabSection, kNoteGNU_stack, kSectionCount }; enum { kNullSymbol, kTextSectionSymbol, kDataSectionSymbol, kBssSectionSymbol, kGlobalSymbol, kSymbolCount }; template void MELFObjectFileImp::Write(std::ofstream &f) { Elf_Ehdr eh = { // e_ident { ELFMAG0, ELFMAG1, ELFMAG2, ELFMAG3, ELF_CLASSXX, ELF_DATAXX, EV_CURRENT, mABI }, ET_REL, // e_type mMachine, // e_machine EV_CURRENT, // e_version 0, // e_entry 0, // e_phoff 0, // e_shoff mFlags, // e_flags sizeof(Elf_Ehdr), // e_ehsize 0, // e_phentsize 0, // e_phnum sizeof(Elf_Shdr), // e_shentsize kSectionCount, // e_shnum kShStrtabSection // e_shstrndx }; uint32_t data_offset = WriteDataAligned(f, &eh, sizeof(eh), 16); std::string strtab; AddNameToNameTable(strtab, ""); // null name Elf_Sym sym = {}; std::vector syms; // kNullSymbol syms.push_back(sym); // text section symbol sym.st_info = ELF32_ST_INFO(STB_LOCAL, STT_SECTION); sym.st_shndx = kTextSection; syms.push_back(sym); // data section symbol sym.st_info = ELF32_ST_INFO(STB_LOCAL, STT_SECTION); sym.st_shndx = kDataSection; syms.push_back(sym); // bss section symbol sym.st_info = ELF32_ST_INFO(STB_LOCAL, STT_SECTION); sym.st_shndx = kBssSection; syms.push_back(sym); uint32_t sym_offset = data_offset; for (const auto &[name, data] : mGlobals) { sym.st_name = AddNameToNameTable(strtab, name); sym.st_value = sym_offset - data_offset; sym.st_size = data.length(); sym.st_info = ELF32_ST_INFO(STB_GLOBAL, STT_OBJECT); sym.st_shndx = kDataSection; syms.push_back(sym); sym_offset = WriteDataAligned(f, data.c_str(), data.length(), 8); } uint32_t data_size = sym_offset; uint32_t symtab_off = sym_offset; assert((sizeof(Elf_Sym) % 8) == 0); uint32_t symtab_size = syms.size() * sizeof(sym); uint32_t strtab_off = WriteDataAligned(f, syms.data(), symtab_size, 8); uint32_t shstrtab_off = WriteDataAligned(f, strtab.c_str(), strtab.length(), 8); std::string shstrtab; (void)AddNameToNameTable(shstrtab, ""); // null name (void)AddNameToNameTable(shstrtab, ".text"); (void)AddNameToNameTable(shstrtab, ".rsrc_data"); (void)AddNameToNameTable(shstrtab, ".bss"); (void)AddNameToNameTable(shstrtab, ".shstrtab"); (void)AddNameToNameTable(shstrtab, ".symtab"); (void)AddNameToNameTable(shstrtab, ".strtab"); (void)AddNameToNameTable(shstrtab, ".note.GNU-stack"); eh.e_shoff = WriteDataAligned(f, shstrtab.c_str(), shstrtab.length(), 16); Elf_Shdr sh[kSectionCount] = { { // kNullSection }, { // kTextSection // sh_name AddNameToNameTable(shstrtab, ".text"), SHT_PROGBITS, // sh_type // sh_flags SHF_ALLOC | SHF_EXECINSTR, 0, // sh_addr data_offset, // sh_offset 0, // sh_size 0, // sh_link 0, // sh_info 4, // sh_addralign 0 // sh_entsize }, { // kDataSection // sh_name AddNameToNameTable(shstrtab, ".rsrc_data"), SHT_PROGBITS, // sh_type // sh_flags SHF_ALLOC | SHF_WRITE, 0, // sh_addr data_offset, // sh_offset data_size, // sh_size 0, // sh_link 0, // sh_info 4, // sh_addralign 0 // sh_entsize }, { // kBssSection // sh_name AddNameToNameTable(shstrtab, ".bss"), SHT_NOBITS, // sh_type // sh_flags SHF_ALLOC | SHF_WRITE, 0, // sh_addr eh.e_shoff, // sh_offset 0, // sh_size 0, // sh_link 0, // sh_info 4, // sh_addralign 0 // sh_entsize }, { // kShStrtabSection // sh_name AddNameToNameTable(shstrtab, ".shstrtab"), SHT_STRTAB, // sh_type 0, // sh_flags 0, // sh_addr shstrtab_off, // sh_offset Elf32_Word(shstrtab.length()), // sh_size 0, // sh_link 0, // sh_info 1, // sh_addralign 0 // sh_entsize }, { // kSymtabSection // sh_name AddNameToNameTable(shstrtab, ".symtab"), SHT_SYMTAB, // sh_type // sh_flags 0, 0, // sh_addr symtab_off, // sh_offset symtab_size, // sh_size 6, // sh_link kGlobalSymbol, // sh_info 8, // sh_addralign sizeof(Elf_Sym) // sh_entsize }, { // kStrtabSection // sh_name AddNameToNameTable(shstrtab, ".strtab"), SHT_STRTAB, // sh_type // sh_flags 0, 0, // sh_addr strtab_off, // sh_offset Elf32_Word(strtab.length()), // sh_size 0, // sh_link 0, // sh_info 1, // sh_addralign 0 // sh_entsize }, { // kStrtabSection // sh_name AddNameToNameTable(shstrtab, ".note.GNU-stack"), SHT_STRTAB, // sh_type // sh_flags 0, 0, // sh_addr 0, // sh_offset 0, // sh_size 0, // sh_link 0, // sh_info 1, // sh_addralign 0 // sh_entsize }, }; WriteDataAligned(f, sh, sizeof(sh), 1); f.flush(); f.seekp(0); WriteDataAligned(f, &eh, sizeof(eh)); } #endif // -------------------------------------------------------------------- // PE/COFF #ifndef IMAGE_FILE_MACHINE_AMD64 enum COFF_Machine : uint16_t { IMAGE_FILE_MACHINE_AMD64 = 0x8664, IMAGE_FILE_MACHINE_I386 = 0x14c, IMAGE_FILE_MACHINE_ARM64 = 0xaa64 }; enum COFF_HeaderCharacteristics : uint16_t { IMAGE_FILE_RELOCS_STRIPPED = 0x0001, IMAGE_FILE_LINE_NUMS_STRIPPED = 0x0004 }; enum COFF_SectionHeaderCharacteristics : uint32_t { IMAGE_SCN_CNT_CODE = 0x00000020, IMAGE_SCN_LNK_INFO = 0x00000200, IMAGE_SCN_LNK_REMOVE = 0x00000800, IMAGE_SCN_CNT_INITIALIZED_DATA = 0x00000040, IMAGE_SCN_CNT_UNINITIALIZED_DATA = 0x00000080, IMAGE_SCN_ALIGN_1BYTES = 0x00100000, IMAGE_SCN_ALIGN_8BYTES = 0x00400000, IMAGE_SCN_ALIGN_16BYTES = 0x00500000, IMAGE_SCN_MEM_EXECUTE = 0x20000000, IMAGE_SCN_MEM_READ = 0x40000000, IMAGE_SCN_MEM_WRITE = 0x80000000 }; enum COFF_StorageClass : uint8_t { IMAGE_SYM_CLASS_EXTERNAL = 0x02, IMAGE_SYM_CLASS_STATIC = 0x03, IMAGE_SYM_CLASS_FILE = 0x67 }; #endif struct COFF_Header { uint16_t machine = IMAGE_FILE_MACHINE_AMD64; uint16_t numberOfSections; uint32_t timeDateStamp = 0; uint32_t pointerToSymbolTable; uint32_t numberOfSymbols; uint16_t sizeOfOptionalHeader; uint16_t characteristics = 0; }; union COFF_Name { char str[8] = ""; struct { uint32_t _filler_; uint32_t offset; }; }; static_assert(sizeof(COFF_Header) == 20, "COFF_Header size should be 20 bytes"); struct COFF_SectionHeader { COFF_Name name; uint32_t virtualSize; uint32_t virtualAddress; uint32_t sizeOfRawData; uint32_t pointerToRawData; uint32_t pointerToRelocations; uint32_t pointerToLineNumbers; uint16_t numberOfRelocations; uint16_t numberOfLineNumbers; uint32_t characteristics; }; static_assert(sizeof(COFF_SectionHeader) == 40, "Section headers should be 40 bytes"); /// \brief COFF symbol table entry, should be 18 bytes... right... struct COFF_Symbol { COFF_Name name; uint32_t value; int16_t sectionNumber; uint16_t type; uint8_t storageClass; uint8_t numberOfAuxSymbols; uint16_t _filler_; }; static_assert(sizeof(COFF_Symbol) == 20, "Symbols should be 18 bytes, plus that filler of 2 bytes which is not written..."); struct COFF_Relocation { uint32_t virtualAddress; uint32_t symbolTableIndex; uint16_t type; }; // -------------------------------------------------------------------- struct MCOFFObjectFileImp : public MObjectFileImp { using swapper = Swap::lsb_swapper; virtual void Write(std::ofstream &inFile) override; MCOFFObjectFileImp(uint16_t machine) : MObjectFileImp() , mMachine(machine) { } uint16_t mMachine; }; void MCOFFObjectFileImp::Write(std::ofstream &f) { // Start by allocating a header COFF_Header header = { mMachine, // machine 0, // numberOfSections 0, // timeDateStamp 0, // pointerToSymbolTable 0, // numberOfSymbols 0, // sizeOfOptionalHeader 0, // characteristics }; std::string strtab; auto addName = [&strtab](const std::string &name) { COFF_Name result{}; if (name.length() <= 8) name.copy(result.str, name.length()); else result.offset = 4 + AddNameToNameTable(strtab, name); return result; }; auto sectionHeaderStart = WriteDataAligned(f, &header, sizeof(header), 1); COFF_SectionHeader sectionHeaders[] = { { addName(".rdata"), // name 0, // virtualSize 0, // virtualAddress 0, // sizeOfRawData 0, // pointerToRawData 0, // pointerToRelocations 0, // pointerToLineNumbers 0, // numberOfRelocations 0, // numberOfLineNumbers IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_ALIGN_8BYTES | IMAGE_SCN_MEM_READ // characteristics }, { addName(".rdata$z"), // name 0, // virtualSize 0, // virtualAddress 0, // sizeOfRawData 0, // pointerToRawData 0, // pointerToRelocations 0, // pointerToLineNumbers 0, // numberOfRelocations 0, // numberOfLineNumbers IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_ALIGN_1BYTES | IMAGE_SCN_MEM_READ // characteristics } }; std::vector symbols{}; auto rawDataOffset = WriteDataAligned(f, sectionHeaders, sizeof(sectionHeaders)); auto offset = rawDataOffset; for (const auto &[name, data] : mGlobals) { symbols.emplace_back( COFF_Symbol{ addName(mMachine == IMAGE_FILE_MACHINE_I386 ? '_' + name : name), offset - rawDataOffset, 1, 0, IMAGE_SYM_CLASS_EXTERNAL }); offset = WriteDataAligned(f, data.data(), static_cast(data.size())); } auto dataSize = offset - rawDataOffset; auto padding = 8 - dataSize % 8; if (padding == 8) padding = 0; auto rawDataSize = dataSize + padding; auto rawData2Offset = WriteDataAligned(f, std::string('\0', 8).data(), padding); using namespace std::literals; std::string package_string = kProjectName + " "s + kVersionNumber; offset = WriteDataAligned(f, package_string.data(), static_cast(package_string.length())); auto rawData2Size = offset - rawData2Offset; auto symbolTableOffset = offset; // We can now fill in the blanks sectionHeaders[0].pointerToRawData = rawDataOffset; sectionHeaders[0].sizeOfRawData = rawDataSize; sectionHeaders[1].pointerToRawData = rawData2Offset; sectionHeaders[1].sizeOfRawData = rawData2Size; // create the rest of the symbols symbols.emplace_back( COFF_Symbol{ addName(".rdata"), 0, 1, 0, IMAGE_SYM_CLASS_STATIC, 1 }); COFF_Name n1{}; n1._filler_ = dataSize; symbols.emplace_back(COFF_Symbol{ n1 }); symbols.emplace_back( COFF_Symbol{ addName(".rdata$z"), 0, 2, 0, IMAGE_SYM_CLASS_STATIC, 1 }); COFF_Name n2{}; n2._filler_ = dataSize; symbols.emplace_back(COFF_Symbol{ n2 }); for (auto &sym : symbols) WriteDataAligned(f, &sym, 18); uint32_t strTabSize = static_cast(strtab.size() + 4); WriteDataAligned(f, &strTabSize, sizeof(strTabSize)); WriteDataAligned(f, strtab.data(), static_cast(strtab.size() + 1)); // write section headers f.seekp(sectionHeaderStart); WriteDataAligned(f, sectionHeaders, sizeof(sectionHeaders)); // Write header f.seekp(0); header.numberOfSections = sizeof(sectionHeaders) / sizeof(COFF_SectionHeader); header.pointerToSymbolTable = symbolTableOffset; header.numberOfSymbols = static_cast(symbols.size()); WriteDataAligned(f, &header, sizeof(header)); } // -------------------------------------------------------------------- #if __has_include() MObjectFileImp *MObjectFileImp::Create(elf_config elfc) { MObjectFileImp *result = nullptr; if (elfc.elf_class == ELFCLASS32 and elfc.elf_data == ELFDATA2LSB) result = new MELFObjectFileImp(elfc.elf_machine, elfc.elf_abi, elfc.elf_flags); else if (elfc.elf_class == ELFCLASS32 and elfc.elf_data == ELFDATA2MSB) result = new MELFObjectFileImp(elfc.elf_machine, elfc.elf_abi, elfc.elf_flags); else if (elfc.elf_class == ELFCLASS64 and elfc.elf_data == ELFDATA2LSB) result = new MELFObjectFileImp(elfc.elf_machine, elfc.elf_abi, elfc.elf_flags); else if (elfc.elf_class == ELFCLASS64 and elfc.elf_data == ELFDATA2MSB) result = new MELFObjectFileImp(elfc.elf_machine, elfc.elf_abi, elfc.elf_flags); else { std::cerr << "Unsupported ELF class and/or data " << elfc.elf_class << ", " << elfc.elf_data << std::endl; exit(1); } return result; } #endif // -------------------------------------------------------------------- class MObjectFile { public: #if __has_include() MObjectFile(elf_config elfc) : mImpl(MObjectFileImp::Create(elfc)) { } #endif MObjectFile(uint16_t machine) : mImpl(new MCOFFObjectFileImp(machine)) { } ~MObjectFile(); void AddGlobal(const std::string &inName, const void *inData, uint32_t inSize); void Write(std::ofstream &inFile); private: MObjectFileImp *mImpl; }; MObjectFile::~MObjectFile() { delete mImpl; } void MObjectFile::AddGlobal(const std::string &inName, const void *inData, uint32_t inSize) { MObjectFileImp::MGlobal g; g.name = inName; g.data.assign(reinterpret_cast(inData), inSize); mImpl->mGlobals.push_back(g); } void MObjectFile::Write(std::ofstream &inFile) { if (mImpl == nullptr) throw std::runtime_error("nullptr error"); mImpl->Write(inFile); } // -------------------------------------------------------------------- class MResourceFile { public: MResourceFile(const std::string &prefix) : mPrefix(prefix) { mIndex.push_back({}); mName.push_back(0); } void Write(MObjectFile &objFile); void Add(const fs::path &inPath, const fs::path &inFile); private: void AddEntry(fs::path inPath, const char *inData, uint32_t inSize); std::vector mIndex; std::vector mData, mName; std::string mPrefix; }; void MResourceFile::AddEntry(fs::path inPath, const char *inData, uint32_t inSize) { uint32_t node = 0; // start at root for (fs::path::iterator p = inPath.begin(); p != inPath.end(); ++p) { if (*p == ".") // flatten continue; // no such child? Add it and continue if (mIndex[node].m_child == 0) { mrsrc::rsrc_imp child = {}; child.m_name = static_cast(mName.size()); std::string n = p->string(); copy(n.begin(), n.end(), std::back_inserter(mName)); mName.push_back(0); mIndex[node].m_child = static_cast(mIndex.size()); mIndex.push_back(child); node = mIndex[node].m_child; continue; } // lookup the path element in the current directory uint32_t next = mIndex[node].m_child; for (;;) { const char *name = mName.data() + mIndex[next].m_name; // if this is the one we're looking for, break out of the loop if (*p == name) { node = next; break; } // if there is a next element, loop if (mIndex[next].m_next != 0) { next = mIndex[next].m_next; continue; } // not found, create it mrsrc::rsrc_imp n = {}; n.m_name = static_cast(mName.size()); std::string s = p->string(); copy(s.begin(), s.end(), back_inserter(mName)); mName.push_back(0); node = static_cast(mIndex.size()); mIndex[next].m_next = node; mIndex.push_back(n); break; } } assert(node != 0); assert(node < mIndex.size()); mIndex[node].m_size = inSize; mIndex[node].m_data = static_cast(mData.size()); copy(inData, inData + inSize, back_inserter(mData)); while ((mData.size() % 8) != 0) mData.push_back('\0'); } void MResourceFile::Add(const fs::path &inPath, const fs::path &inFile) { if (fs::is_directory(inFile)) { fs::path ns = inPath / inFile.filename(); fs::path cwd = fs::current_path(); fs::current_path(inFile); for (auto i = fs::directory_iterator(fs::current_path()); i != fs::directory_iterator(); ++i) Add(ns, i->path().filename()); fs::current_path(cwd); } else { if (VERBOSE > 0) std::cerr << "adding " << inFile << " as " << inPath / inFile.filename() << std::endl; std::ifstream f(inFile, std::ios::binary); if (not f.is_open()) throw std::runtime_error("Could not open data file \'" + inFile.string() + '\''); std::filebuf *b = f.rdbuf(); uint32_t size = static_cast(b->pubseekoff(0, std::ios::end, std::ios::in)); b->pubseekoff(0, std::ios::beg, std::ios::in); std::vector text(size); b->sgetn(text.data(), size); f.close(); AddEntry(inPath / inFile.filename(), text.data(), size); } } void MResourceFile::Write(MObjectFile &obj) { obj.AddGlobal(mPrefix + "Index", mIndex.data(), static_cast(mIndex.size() * sizeof(mrsrc::rsrc_imp))); obj.AddGlobal(mPrefix + "Data", mData.data(), static_cast(mData.size())); obj.AddGlobal(mPrefix + "Name", mName.data(), static_cast(mName.size())); } // -------------------------------------------------------------------- #if __has_include() elf_config get_elf_options(std::filesystem::path object_file) { elf_config elfc{}; int fd = open(object_file.c_str(), O_RDONLY); if (fd < 0) { std::cerr << "Error opening template file " << std::quoted(object_file.string()) << '\n'; exit(1); } unsigned char e_ident[16]; if (read(fd, e_ident, sizeof(e_ident)) == sizeof(e_ident) and e_ident[EI_MAG0] == ELFMAG0 and e_ident[EI_MAG1] == ELFMAG1 and e_ident[EI_MAG2] == ELFMAG2 and e_ident[EI_MAG3] == ELFMAG3) { // Yes, we're an ELF! elfc.elf_class = e_ident[EI_CLASS]; elfc.elf_data = e_ident[EI_DATA]; if (e_ident[EI_ABIVERSION]) elfc.elf_abi = e_ident[EI_ABIVERSION]; lseek(fd, 0, SEEK_SET); switch (elfc.elf_class) { case ELFCLASS32: { Elf32_Ehdr hdr; if (read(fd, &hdr, sizeof(hdr)) == sizeof(Elf32_Ehdr)) { elfc.elf_machine = hdr.e_machine; elfc.elf_flags = hdr.e_flags; } break; } case ELFCLASS64: { Elf64_Ehdr hdr; if (read(fd, &hdr, sizeof(hdr)) == sizeof(Elf64_Ehdr)) { elfc.elf_machine = hdr.e_machine; elfc.elf_flags = hdr.e_flags; } break; } default: std::cerr << "Unknown ELF class" << std::endl; } } return elfc; } #endif // -------------------------------------------------------------------- int main(int argc, char *argv[]) { auto &config = mcfp::config::instance(); config.init( "usage: mrc [options] -o output file1 [file2...]", mcfp::make_option("help,h", "Display help message"), mcfp::make_option("version", "Print version"), mcfp::make_option("output,o", "Output file, this file is in the default object file format for this OS."), mcfp::make_option("header", "This will print out the header file you need to include in your program to access your resources"), mcfp::make_option("root", "Root path for the stored data (in the final resource data structure"), mcfp::make_option("resource-prefix", "gResource", "Prefix for the name of the global variables, default is gResource"), mcfp::make_option("depends,d", "Print a list of files the resource depends upon to the file specified"), #if __has_include() mcfp::make_option("elf-template", "Use the specified file to extract the required ELF flags and properties"), mcfp::make_option("elf-machine", "The ELF machine type to use, default is same as this machine. Use one of the values from elf.h"), mcfp::make_option("elf-class", "ELF class, default is same as this machine. Acceptable values are 1 (32bit) and 2 (64bit)."), mcfp::make_option("elf-data", "ELF endianness, default is same as this machine. Acceptable values are 1 (little-endian, LSB) and 2 (big-endian, MSB)."), mcfp::make_option("elf-abi", "ELF OS ABI value, see file elf.h for values (linux = 3, freebsd = 9)"), mcfp::make_option("elf-flags", "Processor specific flags in the ELF header, e.g. the EABI version for ARM"), #endif mcfp::make_option("coff", "Write a PE/COFF file for Windows, values should be one of x64, x86 or arm64"), mcfp::make_option("verbose,v", "Verbose output")); std::error_code ec; config.parse(argc, argv, ec); if (ec) { std::cerr << ec.message() << std::endl; exit(1); } if (config.has("version")) { write_version_string(std::cout, config.has("verbose")); exit(0); } if (config.has("header")) { mrsrc::rsrc data("mrsrc.h"); std::string text(data.data(), data.size()); if (config.has("output")) { std::ofstream file(config.get("output"), std::ios::binary); if (not file.is_open()) throw std::runtime_error("Could not open output file for writing"); file << text << std::endl; } else std::cout << text << std::endl; exit(0); } if (config.has("help") or config.operands().empty() or not config.has("output")) { std::cout << config << std::endl; exit(config.has("help") ? 0 : 1); } VERBOSE = config.count("verbose"); if (config.has("depends")) { std::vector files; for (auto p : config.operands()) { if (fs::is_directory(p)) { for (auto i = fs::recursive_directory_iterator(p); i != fs::recursive_directory_iterator(); ++i) files.emplace_back(i->path().string()); } else files.emplace_back(p); } std::ofstream depends(config.get("depends")); if (not depends.is_open()) throw std::runtime_error("Could not open depends file for output"); depends << config.get("output") << ": "; for (size_t i = 0; i < files.size(); ++i) { auto &file = files[i]; for (char c : { ' ', '$', '#' }) { for (auto s = file.find(c); s != std::string::npos; s = file.find(c, s)) { file.insert(file.begin() + s, '\\'); s += 2; } } depends << files[i]; if (i + 1 < files.size()) depends << " \\\n\t"; } depends << "\n"; depends.close(); exit(0); } // -------------------------------------------------------------------- // find out the required ELF format. elf_config elfc{}; #if not defined(_WIN32) // Use a template, if specified if (config.has("elf-template")) elfc = get_elf_options(config.get("elf-template")); // use ourselves otherwise else { char exePath[PATH_MAX + 1]; # if __linux or __linux__ elfc.elf_abi = ELFOSABI_LINUX; int r = readlink("/proc/self/exe", exePath, PATH_MAX); # elif __FreeBSD__ elfc.elf_abi = ELFOSABI_FREEBSD; int r = strlen(argv[0]); strcpy(exePath, argv[0]); # elif __GNU__ elfc.elf_abi = ELFOSABI_GNU; int r = readlink("/proc/self/exe", exePath, PATH_MAX); # else # error "Unsupported OS, sorry..." # endif if (r > 0) { exePath[r] = 0; // The NULL is not written by readlink elfc = get_elf_options(exePath); } } #endif std::string ns; if (config.has("root")) ns = config.get("root"); std::string prefix = config.get("resource-prefix"); try { MResourceFile rsrcFile(prefix); for (fs::path i : config.operands()) rsrcFile.Add(ns, i); if (not config.has("output")) return 0; std::ofstream file(config.get("output"), std::ios::binary); if (not file.is_open()) throw std::runtime_error("Could not open output file for writing"); uint16_t win_machine = {}; #if defined(_WIN32) # if defined(_M_AMD64) win_machine = IMAGE_FILE_MACHINE_AMD64; # elif defined(_M_ARM64) win_machine = IMAGE_FILE_MACHINE_ARM64; # elif defined(_M_IX86) win_machine = IMAGE_FILE_MACHINE_I386; # endif #endif if (config.has("coff")) { if (config.get("coff") == "x64") win_machine = IMAGE_FILE_MACHINE_AMD64; else if (config.get("coff") == "arm64") win_machine = IMAGE_FILE_MACHINE_ARM64; else if (config.get("coff") == "x86") win_machine = IMAGE_FILE_MACHINE_I386; else throw std::runtime_error("Unsupported machine for COFF: " + config.get("coff")); } if (config.has("elf-machine")) elfc.elf_machine = config.get("elf-machine"); if (config.has("elf-class")) elfc.elf_class = config.get("elf-class"); if (config.has("elf-data")) elfc.elf_data = config.get("elf-data"); if (config.has("elf-abi")) elfc.elf_abi = config.get("elf-abi"); if (config.has("elf-flags")) elfc.elf_flags = config.get("elf-flags"); if (win_machine and elfc == elf_config{}) { MObjectFile obj(win_machine); rsrcFile.Write(obj); obj.Write(file); } else #if __has_include() { MObjectFile obj(elfc); rsrcFile.Write(obj); obj.Write(file); } #else throw std::runtime_error("Could not create resource file, probably you're trying to create a ELF resource file on Windows?"); #endif } catch (const std::exception &ex) { std::cerr << "Error executing mrc: " << ex.what() << std::endl; exit(1); } return 0; } mrc-1.3.16/src/mrsrc.h000066400000000000000000000230661501502116100144500ustar00rootroot00000000000000/*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2006-2023 Maarten L. Hekkelman * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #pragma once #include #include #include #include #include #include /* Resources are data sources for the application. They are retrieved by name. Basic usage: mrsrc::rsrc rsrc("dialogs/my-cool-dialog.glade"); if (rsrc) { GladeXML* glade = glade_xml_new_from_buffer(rsrc.data(), rsrc.size(), NULL, "japi"); ... } Alternative, to loop over all resources: mrsrc rsrc; // <- the root resource for (rsrc child: rsrc) std::cout << child.name() << std::endl; ------------------------------------------------- Stream interface: mrsrc::streambuf rb("data/my-text.txt"); std::istream is(&rb); std::string line; while (std::gettline(is, line)) std::cout << line << std::endl; */ namespace mrsrc { /// \brief Internal data structure as generated by mrc struct rsrc_imp { unsigned int m_next; unsigned int m_child; unsigned int m_name; unsigned int m_size; unsigned int m_data; }; } // namespace mrsrc // The following three variables are generated by mrc: extern "C" const mrsrc::rsrc_imp gResourceIndex[]; extern "C" const char gResourceData[]; extern "C" const char gResourceName[]; namespace mrsrc { /// \brief Class mrsrc::rsrc contains a pointer to the data in the /// resource, as well as offering an iterator interface to its /// children. class rsrc { public: rsrc() : m_impl(gResourceIndex) { } rsrc(const rsrc &other) : m_impl(other.m_impl) { } rsrc &operator=(const rsrc &other) { m_impl = other.m_impl; return *this; } rsrc(std::filesystem::path path); std::string name() const { return m_impl ? gResourceName + m_impl->m_name : ""; } const char *data() const { return m_impl ? gResourceData + m_impl->m_data : nullptr; } unsigned long size() const { return m_impl ? m_impl->m_size : 0; } explicit operator bool() const { return m_impl != nullptr and m_impl->m_size > 0; } template class iterator_t { public: using iterator_category = std::input_iterator_tag; using value_type = RSRC; using difference_type = std::ptrdiff_t; using pointer = value_type *; using reference = value_type &; iterator_t(const rsrc_imp *cur) : m_cur(cur) { } iterator_t(const iterator_t &i) : m_cur(i.m_cur) { } iterator_t &operator=(const iterator_t &i) { m_cur = i.m_cur; return *this; } reference operator*() { return m_cur; } pointer operator->() { return &m_cur; } iterator_t &operator++() { if (m_cur.m_impl->m_next) m_cur.m_impl = gResourceIndex + m_cur.m_impl->m_next; else m_cur.m_impl = nullptr; return *this; } iterator_t operator++(int) { auto tmp(*this); this->operator++(); return tmp; } bool operator==(const iterator_t &rhs) const { return m_cur.m_impl == rhs.m_cur.m_impl; } bool operator!=(const iterator_t &rhs) const { return m_cur.m_impl != rhs.m_cur.m_impl; } private: value_type m_cur; }; using iterator = iterator_t; iterator begin() const { const rsrc_imp *impl = nullptr; if (m_impl and m_impl->m_child) impl = gResourceIndex + m_impl->m_child; return iterator(impl); } iterator end() const { return iterator(nullptr); } private: rsrc(const rsrc_imp *imp) : m_impl(imp) { } const rsrc_imp *m_impl; }; inline rsrc::rsrc(std::filesystem::path p) { m_impl = gResourceIndex; // using std::filesytem::path would have been natural here of course... auto pb = p.begin(); auto pe = p.end(); while (m_impl != nullptr and pb != pe) { auto name = *pb++; const rsrc_imp *impl = nullptr; for (rsrc child : *this) { if (child.name() == name) { impl = child.m_impl; break; } } m_impl = impl; } if (pb != pe) // not found m_impl = nullptr; } // -------------------------------------------------------------------- template class basic_streambuf : public std::basic_streambuf { public: typedef CharT char_type; typedef Traits traits_type; typedef typename traits_type::int_type int_type; typedef typename traits_type::pos_type pos_type; typedef typename traits_type::off_type off_type; /// \brief constructor taking a \a path to the resource in memory basic_streambuf(const std::string &path) : m_rsrc(path) { init(); } /// \brief constructor taking a \a rsrc basic_streambuf(const rsrc &rsrc) : m_rsrc(rsrc) { init(); } basic_streambuf(const basic_streambuf &) = delete; basic_streambuf(basic_streambuf &&rhs) : basic_streambuf(rhs.m_rsrc) { } basic_streambuf &operator=(const basic_streambuf &) = delete; basic_streambuf &operator=(basic_streambuf &&rhs) { swap(rhs); return *this; } void swap(basic_streambuf &rhs) { std::swap(m_begin, rhs.m_begin); std::swap(m_end, rhs.m_end); std::swap(m_current, rhs.m_current); } /// \brief Analogous to is_open of an ifstream_buffer, return true if the resource is valid bool is_valid() const { return static_cast(m_rsrc); } private: void init() { if (m_rsrc) { m_begin = reinterpret_cast(m_rsrc.data()); m_end = reinterpret_cast(m_rsrc.data() + m_rsrc.size()); m_current = m_begin; } } int_type underflow() { if (m_current == m_end) return traits_type::eof(); return traits_type::to_int_type(*m_current); } int_type uflow() { if (m_current == m_end) return traits_type::eof(); return traits_type::to_int_type(*m_current++); } int_type pbackfail(int_type ch) { if (m_current == m_begin or (ch != traits_type::eof() and ch != m_current[-1])) return traits_type::eof(); return traits_type::to_int_type(*--m_current); } std::streamsize showmanyc() { assert(std::less_equal()(m_current, m_end)); return m_end - m_current; } pos_type seekoff(off_type off, std::ios_base::seekdir dir, std::ios_base::openmode /*which*/) { switch (dir) { case std::ios_base::beg: m_current = m_begin + off; break; case std::ios_base::end: m_current = m_end + off; break; case std::ios_base::cur: m_current += off; break; default: break; } if (m_current < m_begin) m_current = m_begin; if (m_current > m_end) m_current = m_end; return m_current - m_begin; } pos_type seekpos(pos_type pos, std::ios_base::openmode /*which*/) { m_current = m_begin + pos; if (m_current < m_begin) m_current = m_begin; if (m_current > m_end) m_current = m_end; return m_current - m_begin; } private: rsrc m_rsrc; const char_type *m_begin = nullptr; const char_type *m_end = nullptr; const char_type *m_current = nullptr; }; using streambuf = basic_streambuf>; // -------------------------------------------------------------------- // class mrsrc::istream template class basic_istream : public std::basic_istream { public: typedef CharT char_type; typedef Traits traits_type; typedef typename traits_type::int_type int_type; typedef typename traits_type::pos_type pos_type; typedef typename traits_type::off_type off_type; private: using __streambuf_type = basic_streambuf; using __istream_type = std::basic_istream; __streambuf_type m_buffer; public: basic_istream(const std::string &path) : basic_istream(rsrc(path)) { } basic_istream(const rsrc &resource) : __istream_type(&m_buffer) , m_buffer(resource) { if (resource) this->init(&m_buffer); else __istream_type::setstate(std::ios_base::badbit); } basic_istream(const basic_istream &) = delete; basic_istream(basic_istream &&rhs) : __istream_type(std::move(rhs)) , m_buffer(std::move(rhs.m_buffer)) { __istream_type::set_rdbuf(&m_buffer); } basic_istream &operator=(const basic_istream &) = delete; basic_istream &operator=(basic_istream &&rhs) { __istream_type::operator=(std::move(rhs)); m_buffer = std::move(rhs.m_buffer); return *this; } void swap(basic_istream &rhs) { __istream_type::swap(rhs); m_buffer.swap(rhs.m_buffer); } __streambuf_type *rdbuf() const { return const_cast<__streambuf_type *>(&m_buffer); } }; using istream = basic_istream>; } // namespace mrsrc mrc-1.3.16/src/rsrc-test.cpp000066400000000000000000000006031501502116100155730ustar00rootroot00000000000000#include #include #include "mrsrc.h" int main() { mrsrc::rsrc eerste("eerste"); if (eerste) std::cout << std::string(eerste.data(), eerste.size()) << std::endl; else std::cout << "not found" << std::endl; mrsrc::rsrc error_rsrc("invalid"); assert(not error_rsrc); assert(error_rsrc.data() == nullptr); assert(error_rsrc.size() == 0); return 0; }