diff --git a/.github/workflows/cff-validator.yml b/.github/workflows/cff-validator.yml new file mode 100644 index 0000000..83d75d0 --- /dev/null +++ b/.github/workflows/cff-validator.yml @@ -0,0 +1,25 @@ +name: Validate CITATION.cff + +on: + push: + paths: + - 'CITATION.cff' + - '.github/workflows/cff-validator.yml' + pull_request: + paths: + - 'CITATION.cff' + - '.github/workflows/cff-validator.yml' + workflow_dispatch: + +jobs: + Validate-CITATION-cff: + runs-on: ubuntu-latest + name: Validate CITATION.cff + env: + GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + - name: Validate CITATION.cff + uses: dieghernan/cff-validator@v3 diff --git a/.github/workflows/ctest.yml b/.github/workflows/ctest.yml index c2a5dcc..e610fcd 100644 --- a/.github/workflows/ctest.yml +++ b/.github/workflows/ctest.yml @@ -8,17 +8,32 @@ on: branches: ["main", "dev", "CrossPlatform-And-PropLib2.0"] workflow_dispatch: +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }} + cancel-in-progress: true + # Define the matrix for different operating systems jobs: build-and-test: - name: ${{ matrix.os }} / CMake ${{ matrix.cmakeVersion }} + name: ${{ matrix.os }} / ${{ matrix.architecture }} / CMake ${{ matrix.cmakeVersion }} runs-on: ${{ matrix.os }} strategy: matrix: - os: [ubuntu-latest, macos-latest, windows-latest] - # CMake >= 3.21 is required to use "--preset <presetName>" and discover generators - cmakeVersion: ["3.21", latest] - + os: + - ubuntu-latest + - macos-latest + - windows-latest + architecture: [arm64, x64, x86] + cmakeVersion: ["3.21", latest] # CMake >= 3.21 is required to use "--preset <presetName>" and discover generators + exclude: + - os: macos-latest + architecture: x86 + - os: windows-latest + architecture: arm64 + - os: ubuntu-latest + architecture: arm64 + - os: ubuntu-latest + architecture: x86 steps: - name: Checkout repository uses: actions/checkout@v4 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 347d0ef..d7fbda4 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -15,7 +15,20 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - os: [ubuntu-latest, macos-latest, windows-latest] + os: + - ubuntu-latest + - macos-latest + - windows-latest + - windows-2019 # Used for Windows 32-bit builds + architecture: [arm64, x64] + cmakeVersion: ["3.21", latest] # CMake >= 3.21 is required to use "--preset <presetName>" and discover generators + exclude: # Only use x64 strategies for Linux and Windows + - os: windows-latest + architecture: arm64 + - os: windows-2019 + architecture: arm64 + - os: ubuntu-latest + architecture: arm64 steps: - name: Checkout repository uses: actions/checkout@v4 diff --git a/.gitignore b/.gitignore index 9c069f8..738bc1d 100644 --- a/.gitignore +++ b/.gitignore @@ -6,21 +6,6 @@ Thumbs.db *.o -#################################### -## Shared Library files in Wrappers -#################################### -**.dll -**.so -**.dylib - -###################################### -## Derived files produced by wrappers -###################################### -**.mltbx -**.whl -**.tar.gz -**.nupkg - ################# ## Visual Studio ################# diff --git a/.zenodo.json b/.zenodo.json deleted file mode 100644 index c36a1c1..0000000 --- a/.zenodo.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "creators": [ - { - "orcid": "0000-0002-7417-4009", - "affiliation": "The Institute for Telecommunication Sciences", - "name": "Kozma Jr, William" - } - ], - "license": "NTIA Public Domain", - "title": "Low Frequency / Medium Frequency (LF/MF) Propagation Model", - "upload_type": "software", - "version": "1.0", - "keywords": [ "Study Group 3", "ITS", "Propagation", "LFMF" ], - "communities": [ - { "identifier": "its-proplib" } - ] -} diff --git a/CITATION.cff b/CITATION.cff index 0907da6..589736c 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -16,12 +16,13 @@ authors: - name: >- U.S. Department of Commerce, National Telecommunications and Information Administration, - Institute for Telecommunications Sciences + Institute for Telecommunication Sciences address: 325 Broadway city: Boulder - region: Colorado - post-code: '80305' country: US + post-code: '80305' + region: Colorado + alias: NTIA/ITS email: code@ntia.gov website: 'https://its.ntia.gov' repository-code: 'https://github.com/NTIA/LFMF' @@ -31,6 +32,5 @@ keywords: - its - propagation - lfmf -license: 'NTIA Public Domain' version: 1.0 date-released: '2024-12-01' diff --git a/CMakeLists.txt b/CMakeLists.txt index fd87e1e..a11203f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,6 +10,28 @@ # >=3.14 required for GoogleTest v1.12.x cmake_minimum_required(VERSION 3.14 FATAL_ERROR) +# If on macOS, handle arm64/x86_64 architectures (must be done before project()) +if (APPLE) + # Get the current platform's native architecture + execute_process( + COMMAND uname -m + RESULT_VARIABLE result + OUTPUT_VARIABLE MACOS_NATIVE_ARCHITECTURE + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + message(STATUS "Current macOS architecture is " ${MACOS_NATIVE_ARCHITECTURE}) + + # If running on Apple silicon, try a universal build. Otherwise, a native build. + if ((CMAKE_GENERATOR STREQUAL "Xcode") AND (MACOS_NATIVE_ARCHITECTURE STREQUAL "arm64")) + set(CMAKE_OSX_ARCHITECTURE "arm64;x86_64" CACHE STRING "") + set(PROPLIB_ARCHITECTURE "macOS_universal") + message(STATUS "Configured for universal macOS build") + else () + set(PROPLIB_ARCHITECTURE MACOS_NATIVE_ARCHITECTURE) + message(STATUS "Configured for native macOS build") + endif () +endif () + ########################################### ## PROJECT METADATA ########################################### @@ -23,13 +45,6 @@ project( LANGUAGES "CXX" ) -########################################### -## SPECIFY MULTI-LANGUAGE WRAPPERS -########################################### -set(DOTNET_WRAPPER_DIR "${PROJECT_SOURCE_DIR}/wrap/dotnet") -set(MATLAB_WRAPPER_DIR "${PROJECT_SOURCE_DIR}/wrap/matlab") -set(PYTHON_WRAPPER_DIR "${PROJECT_SOURCE_DIR}/wrap/python") - ########################################### ## CMAKE OPTIONS AND DEFAULTS ########################################### @@ -39,7 +54,6 @@ option(BUILD_DRIVER "Build the command-line driver executable" ON) option(RUN_DRIVER_TESTS "Test the command-line driver executable" ON) option(DOCS_ONLY "Skip all steps except generating the documentation site" OFF) option(RUN_TESTS "Run unit tests for the main library" ON) -option(COPY_TO_WRAPPERS "Copy the compiled shared library into wrapper submodules" ON) ########################################### ## SETUP @@ -53,12 +67,22 @@ add_library(proplib_compiler_flags INTERFACE) target_compile_features(proplib_compiler_flags INTERFACE cxx_std_11) # add compiler warning flags just when building this project via -# the BUILD_INTERFACE genex +# the BUILD_INTERFACE generator expression set(gcc_like_cxx "$<COMPILE_LANG_AND_ID:CXX,ARMClang,AppleClang,Clang,GNU,LCC>") set(msvc_cxx "$<COMPILE_LANG_AND_ID:CXX,MSVC>") target_compile_options(proplib_compiler_flags INTERFACE - "$<${gcc_like_cxx}:$<BUILD_INTERFACE:-Wall;-Wextra;-Wshadow;-Wformat=2;-Wunused>>" - "$<${msvc_cxx}:$<BUILD_INTERFACE:-W3>>" + # For GCC-like compilers in any configuration + "$<${gcc_like_cxx}:$<BUILD_INTERFACE:-Wall;-Wextra;-Wshadow;-Wformat=2;-Wunused>>" + # For GCC-like compilers in Release configurations + "$<${gcc_like_cxx}:$<$<CONFIG:Release>:-O3;-DNDEBUG>>" + # For GCC-like compilers in Debug configurations + "$<${gcc_like_cxx}:$<$<CONFIG:Debug>:-g;-O0>>" + # For MSVC compiler in any configuration + "$<${msvc_cxx}:$<BUILD_INTERFACE:/W3;/Gz>>" + # For MSVC compiler in Release configurations + "$<${msvc_cxx}:$<$<CONFIG:Release>:/O2;/DNDEBUG>>" + # For MSVC compiler in Debug configurations + "$<${msvc_cxx}:$<$<CONFIG:Debug>:/Od;/Zi>>" ) # Enable Hot Reload for MSVC compilers if supported. @@ -67,29 +91,23 @@ if (POLICY CMP0141) set(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT "$<IF:$<AND:$<C_COMPILER_ID:MSVC>,$<CXX_COMPILER_ID:MSVC>>,$<$<CONFIG:Debug,RelWithDebInfo>:EditAndContinue>,$<$<CONFIG:Debug,RelWithDebInfo>:ProgramDatabase>>") endif () -# control where the static and shared libraries are built so that on windows -# we don't need to tinker with the path to run the executable +# control where the static and shared libraries are built set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_SOURCE_DIR}/bin" CACHE STRING "Set the CMAKE Archive Output Directory") set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${PROJECT_SOURCE_DIR}/bin" CACHE STRING "Set the CMAKE Library Output Directory") set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${PROJECT_SOURCE_DIR}/bin" CACHE STRING "Set the CMAKE Runtime Output Directory") -#include(CMakeDependentOption) -#include(GNUInstallDirs) - ########################################## ## BUILD/RUN ########################################## if (NOT DOCS_ONLY) - add_subdirectory(src) # Build the library - - if (COPY_TO_WRAPPERS) # Copy compiled library to wrappers - add_subdirectory(wrap) - endif () + add_subdirectory(src) # Build the shared library if (RUN_TESTS OR RUN_DRIVER_TESTS) + # Set up GoogleTest if (EXISTS "${PROJECT_SOURCE_DIR}/extern/googletest/CMakeLists.txt") enable_testing() set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) + # Ensure GoogleTest is built as a static library if (DEFINED BUILD_SHARED_LIBS AND BUILD_SHARED_LIBS) set(BUILD_SHARED_LIBS_${LIB_NAME} ${BUILD_SHARED_LIBS}) set(BUILD_SHARED_LIBS OFF) @@ -97,7 +115,7 @@ if (NOT DOCS_ONLY) endif () add_subdirectory("${PROJECT_SOURCE_DIR}/extern/googletest" "extern/googletest") include(GoogleTest) - + # Restore initial value of BUILD_SHARED_LIBS if (DEFINED BUILD_SHARED_LIBS_${LIB_NAME}) set(BUILD_SHARED_LIBS ${BUILD_SHARED_LIBS_${LIB_NAME}}) message(STATUS "STATUS: BUILD_SHARED_LIBS is " ${BUILD_SHARED_LIBS} " to build ${LIB_NAME} library.") @@ -110,8 +128,7 @@ if (NOT DOCS_ONLY) ) endif () endif () - - if (RUN_TESTS) # Build and run unit tests + if (RUN_TESTS) # Build and run unit tests add_subdirectory(tests) endif () diff --git a/CMakePresets.json b/CMakePresets.json index 9416ccf..c59db14 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -14,7 +14,8 @@ "cacheVariables": { "CMAKE_BUILD_RPATH_USE_ORIGIN": "ON", "DOCS_ONLY": "OFF", - "RUN_TESTS": "ON" + "RUN_TESTS": "ON", + "BUILD_SHARED_LIBS": "ON" } }, { diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5f75e90..81696d8 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -51,18 +51,16 @@ When complete, features branches should merge into `dev`. ### Git Submodules -Software in the ITS Propagation Library is implemented primarily in C++. Each piece -of software has a primary repository which contains the base C++ implementation, -test data and resources, and common files used by the multi-language wrappers. -Interfaces for additional programming languages are provided in separate repositories, -which are linked to the primary repository as [Git submodules](https://gist.github.com/gitaarik/8735255). -When cloning the primary repository, the submodules are not additionally cloned -by default. This can be done with the `git submodule init` command. Initializing -the submodule as part of the parent repository will let you use the build -configuration from the primary repository to compile the C++ source and place it -appropriately for use by the wrapper code. If you choose to independently clone -the wrapper repository, you will likely need to separately download the compiled -library (for example, a DLL from a GitHub release). +PropLib C++ repositories make use of Git submodules to reference certain development +dependencies, e.g. GoogleTest. Depending on the CMake preset or options used, submodules +may be required to successfully build and/or test the software. When cloning a repository, +submodules are not additionally cloned by default. Use the following commands to initialize +and clone any submodules in a repository: + +```cmd +git submodule init +git submodule update +``` ### Contributing on GitHub @@ -142,8 +140,7 @@ docs/ extern/ ... # External Git submodules/dependencies include/ - <PackageNamespace>/ # Include namespace folder, e.g. "ITS.Propagation.ITM" - <HeaderFiles>.h # Library header files go here, e.g. "ITM.h" and "ErrorCodes.h" + <HeaderFile>.h # Library interface header file goes here, e.g. "ITM.h" src/ <SourceFiles>.cpp # Source files go here, e.g. "LongleyRice.cpp" and "FreeSpaceLoss.cpp" CMakeLists.txt # Configures cross-platform build @@ -153,10 +150,6 @@ tests/ <TestFiles>.cpp # Unit tests, usually one test file per source file. <TestFiles>.h # Any headers used by tests go here as well. CMakeLists.txt # CTest+GTest config. Files containing tests must be included here. -wrap/ - dotnet/ # C#/.NET wrapper submodule. Should contain CMakeLists.txt - matlab/ # MATLAB wrapper submodule. Should contain CMakeLists.txt - python/ # Python wrapper submodule. Should contain CMakeLists.txt CMakeLists.txt # Top-level CMakeLists.txt: project metadata and options CMakePresets.json # Presets for CMake, e.g. "release", "debug", etc. ... @@ -179,7 +172,6 @@ The following CMake options are used for top-level project configuration: | `RUN_DRIVER_TESTS` | `ON` | Test the command-line driver executable | | `DOCS_ONLY` | `OFF` | Skip all steps _except_ generating the documentation site | | `RUN_TESTS` | `ON` | Run unit tests for the main library | -| `COPY_TO_WRAPPERS` | `ON` | Copy the compiled shared library into wrapper submodules | [CMake Presets](https://cmake.org/cmake/help/latest/manual/cmake-presets.7.html) are provided to support common build configurations. These are specified in the @@ -273,8 +265,36 @@ the Doxygen site to GitHub Pages. ### MATLAB Wrappers -Most code in the MATLAB wrapper is actually written in C. In these files, the same -documentation style as noted above for C++ should be used. +MATLAB® wrappers are implemented as toolboxes which interface with the shared library +compiled from C++ source code. The project structure is informed by the best practices +provided by MathWorks® in their [`toolboxdesign` repository](https://github.com/mathworks/toolboxdesign). +Here is an example of how a function may be documented in a MATLAB wrapper. Note the +documentation with code, where input and output arguments are provided for autocompletion. + +```matlab +function y = DoubleTheInput(x) +% DoubleTheInput - produces an output which is twice its input. +% +% Syntax: +% y = DoubleTheInput(x) +% +% Input Arguments: +% x (double) - A number which needs doubling +% +% Output Arguments: +% y (double) - The result, 2*x +% +% Description: +% Functions more complex than this one may warrant an additional, +% longer description. +arguments (Input) + x double +end +arguments (Output) + y double +end +... +``` ### Python Wrappers @@ -302,9 +322,9 @@ def double_the_input(x: float) -> float: return 2 * x ``` -### C#/.NET Wrappers +### .NET Wrappers -In C#/.NET, documentation comments are written in +PropLib .NET wrappers are written in C# and documentation comments are written in [XML format](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/documentation-comments) and are used to generate documentation through tools like Visual Studio. Use `<summary>` tags to provide brief descriptions of classes, constants, functions, etc. Functions should diff --git a/GitHubRepoPublicReleaseApproval.md b/GitHubRepoPublicReleaseApproval.md index d37117c..73760ec 100644 --- a/GitHubRepoPublicReleaseApproval.md +++ b/GitHubRepoPublicReleaseApproval.md @@ -1,23 +1,32 @@ # GitHub Repository Public Release Approval -**Project Name:** Office of Spectrum Management R&D - Propagation Library +**Project Name:** NTIA/OSM Research and Development **Software Name:** Low Frequency / Medium Frequency (LF/MF) Propagation Model -The software identified above, which is contained within the repository this document is stored in, has met the following criteria for public release: +The project identified above, which is contained within the repository this +document is stored in, has met the following criteria for public release: -1. [ ] The project, including the test criteria, meets the requirements defined in the ITS Software Development Publication Policy for making a repository public. The major pre-established criteria for publication are listed below, and the check mark next to each attests that the criterion has been met. - * [ ] Unit tests are available and the software has been tested against the unit tests - * [ ] Any test data necessary for the code and its unit tests to function is included in this GitHub repository, or in a parent repository which includes this one as a Git submodule. - * [ ] This repository contains complete README and CONTRIBUTING files -1. [ ] This repository adheres to the PropLib Template, and is up-to-date with the latest tagged release of the template repository. -1. [ ] All template or placeholder code has been removed -1. [ ] The README.md file has passed editorial review from the ITS Publications Office. -1. [ ] The project complies with the ITS Code Style Guide or an appropriate style guide as agreed to by the sponsor, project lead, or Supervising Division Chief. -1. [ ] Approved disclaimer and licensing language has been included. +1. [ ] The project, including the test criteria, meets the requirements defined +in the ITS Software Development Publication Policy for making a repository public. +The major pre-established criteria for publication are listed below, and the check +mark next to each attests that the criterion has been met. + * [ ] Unit tests are available and the software has been tested against the unit tests. + * [ ] The software can be compiled and/or used on Windows, macOS, and Linux. + * [ ] The repository structure and contents follow from the ITS PropLib template, and + all template or placeholder code has been removed. + * [ ] The repository includes the appropriate `LICENSE.md` file +2. [ ] Any test data necessary for the code and its unit tests to function is included in this +GitHub repository, or in a parent repository which includes this one as a Git submodule. +3. [ ] The README.md file has passed editorial review from the ITS Publications Office. +4. [ ] The project complies with the ITS Code Style Guide or an appropriate style +guide as agreed to by the sponsor, project lead, or Supervising Division Chief. +5. [ ] Approved disclaimer and licensing language has been included. -In order to complete this approval, please create a new branch, upload and commit your version of this Markdown document to that branch, then create a pull request for that branch. The following must login to GitHub and approve that pull request before the pull request can be merged and this repo made public: +In order to complete this approval, please create a new branch, upload and commit +your version of this Markdown document to that branch, then create a pull request +for that branch. The following must login to GitHub and approve that pull request +before the pull request can be merged and this repo made public: -* **Project Lead:** Kozma Jr, William - -* **Supervising Division Chief or Release Authority:** TODO-TEMPLATE +* Project Lead: Kozma Jr, William +* Supervising Division Chief or Release Authority: TODO-TEMPLATE diff --git a/app/tests/CMakeLists.txt b/app/tests/CMakeLists.txt index 2af0fe0..562d928 100644 --- a/app/tests/CMakeLists.txt +++ b/app/tests/CMakeLists.txt @@ -23,7 +23,7 @@ target_include_directories( target_link_libraries(${DRIVER_TEST_NAME} ${LIB_NAME}) # Make driver executable location available to source -add_definitions(-DDRIVER_LOCATION="$<TARGET_FILE:${DRIVER_NAME}>") +add_compile_definitions(DRIVER_LOCATION="$<TARGET_FILE:${DRIVER_NAME}>") ########################################### ## SET UP AND DISCOVER TESTS diff --git a/docs/doxy_mainpage.md b/docs/doxy_mainpage.md index d02612c..9084066 100644 --- a/docs/doxy_mainpage.md +++ b/docs/doxy_mainpage.md @@ -11,7 +11,7 @@ library or take it as a dependency. On the wiki, you'll find installation instructions, usage guides, and code examples for this and other software within the NTIA/ITS Propagation Library. Further, the wiki includes instructions for using this library from other software languages, -including Python, MATLAB, and C#/.NET. +including bindings for Python, MATLAB, and .NET. ## Site Navigation diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e6a5f3f..d8377ee 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -33,6 +33,8 @@ else () add_library(${LIB_NAME} ${LIB_FILES}) endif () + + # Add the include directory target_include_directories(${LIB_NAME} PUBLIC "${LIB_HEADERS}") diff --git a/src/ReturnCodes.cpp b/src/ReturnCodes.cpp index 639308b..c833d81 100644 --- a/src/ReturnCodes.cpp +++ b/src/ReturnCodes.cpp @@ -66,7 +66,7 @@ std::string GetReturnStatus(int code) { * @param[in] code Integer return code. * @return A status message corresponding to the input code. ******************************************************************************/ -DLLEXPORT char *GetReturnStatusCharArray(const int code) { +char *GetReturnStatusCharArray(const int code) { const std::string msg = GetReturnStatus(code); char *c_msg = new char[msg.size() + 1]; #ifdef _WIN32 @@ -82,7 +82,7 @@ DLLEXPORT char *GetReturnStatusCharArray(const int code) { * * @param[in] c_msg The status message C-style string to delete ******************************************************************************/ -DLLEXPORT void FreeReturnStatusCharArray(char *c_msg) { +void FreeReturnStatusCharArray(char *c_msg) { delete[] c_msg; } diff --git a/wrap/CMakeLists.txt b/wrap/CMakeLists.txt deleted file mode 100644 index 297d528..0000000 --- a/wrap/CMakeLists.txt +++ /dev/null @@ -1,28 +0,0 @@ -########################################### -## COPY COMPILED LIBRARY TO WRAPPERS -########################################### -# Wrapper directories are configured as variables -# in the top-level CMakeLists.txt file. This file -# checks if `CMakeLists.txt` exists in each wrapper -# directory, and if so, adds that subdirectory. - -# C#/.NET -if (EXISTS "${DOTNET_WRAPPER_DIR}/CMakeLists.txt") - add_subdirectory(${DOTNET_WRAPPER_DIR}) -else () - message(STATUS "Skipping copying compiled library to C#/.NET wrapper: submodule not initialized.") -endif () - -# MATLAB -if (EXISTS "${MATLAB_WRAPPER_DIR}/CMakeLists.txt") - add_subdirectory(${MATLAB_WRAPPER_DIR}) -else () - message(STATUS "Skipping copying compiled library to MATLAB wrapper: submodule not initialized") -endif () - -# Python -if (EXISTS "${PYTHON_WRAPPER_DIR}/CMakeLists.txt") - add_subdirectory(${PYTHON_WRAPPER_DIR}) -else () - message(STATUS "Skipping copying compiled library to Python wrapper: submodule not initialized") -endif () \ No newline at end of file