Browse Source

Merge pull request #54 from spinda/cmake-arduino

cmake: Add arduino cross-compiling and package generation (`hydrogen-crypto.zip`)
Frank Denis 6 years ago
parent
commit
e7fb9b655e
4 changed files with 232 additions and 55 deletions
  1. 0 1
      .gitignore
  2. 106 54
      CMakeLists.txt
  3. 126 0
      cmake/arduino-avr-toolchain.cmake
  4. 0 0
      cmake/hydrogen-config.cmake.in

+ 0 - 1
.gitignore

@@ -1,5 +1,4 @@
 *.bc
-*.cmake
 *.dSYM
 *.done
 *.final

+ 106 - 54
CMakeLists.txt

@@ -1,26 +1,63 @@
-cmake_minimum_required(VERSION 3.10)
+cmake_minimum_required(VERSION 3.12)
 
-project(hydrogen
-        LANGUAGES C)
+project(hydrogen LANGUAGES C)
 
 include(CMakePackageConfigHelpers)
 include(GNUInstallDirs)
 
-if(NOT CMAKE_CROSSCOMPILING)
-    set(default_build_arch native)
-endif()
+string(TOUPPER "${PROJECT_NAME}" setting_prefix)
+function(get_setting setting_name setting_type setting_description)
+    string(TOUPPER "${setting_prefix}_${setting_name}" setting_external_name)
+    set("${setting_external_name}" "" CACHE "${setting_type}" "${setting_description}")
+    set("${setting_name}" "${${setting_external_name}}" PARENT_SCOPE)
+endfunction()
+
+# Project files
+
+set(source_files
+        "${PROJECT_NAME}.c"
+        "impl/common.h"
+        "impl/core.h"
+        "impl/gimli-core.h"
+        "impl/gimli-core/portable.h"
+        "impl/gimli-core/sse2.h"
+        "impl/hash.h"
+        "impl/${PROJECT_NAME}_p.h"
+        "impl/kdf.h"
+        "impl/kx.h"
+        "impl/pwhash.h"
+        "impl/random.h"
+        "impl/secretbox.h"
+        "impl/sign.h"
+        "impl/x25519.h")
+
+set(header_files
+        "${PROJECT_NAME}.h")
 
-set(BUILD_ARCH "${default_build_arch}" CACHE STRING
-        "Target system architecture (fed to the compiler's -march=...)")
+set(test_files
+        "tests/tests.c")
+
+set(arduino_files
+        "library.properties")
+
+# Compile options
+
+get_setting(target_arch STRING "Target system architecture (fed to the compiler's -march=...).")
+if(NOT target_arch AND NOT CMAKE_CROSSCOMPILING)
+    set(target_arch native)
+endif()
 
 set(compile_options
+        # GNU, Clang
         $<$<OR:$<C_COMPILER_ID:AppleClang>,$<C_COMPILER_ID:Clang>,$<C_COMPILER_ID:GNU>>:
             # Optimizations
-            -Os $<$<BOOL:${BUILD_ARCH}>:-march=${BUILD_ARCH}> -fno-exceptions
+            -Os $<$<BOOL:${target_arch}>:-march=${target_arch}> -fno-exceptions
             # Warnings
             -Wall -Wextra -Wmissing-prototypes -Wdiv-by-zero -Wbad-function-cast -Wcast-align
             -Wcast-qual -Wfloat-equal -Wmissing-declarations -Wnested-externs -Wno-unknown-pragmas
             -Wpointer-arith -Wredundant-decls -Wstrict-prototypes -Wswitch-enum -Wno-type-limits>
+
+        # MSVC
         $<$<C_COMPILER_ID:MSVC>:
             # Optimizations
             /Os /EHsc
@@ -31,76 +68,66 @@ set(compile_options
             /wd4310 # suppress warning "cast truncates constant value"
         >)
 
-set(source_files
-        "${PROJECT_SOURCE_DIR}/${PROJECT_NAME}.c"
-        "${PROJECT_SOURCE_DIR}/impl/common.h"
-        "${PROJECT_SOURCE_DIR}/impl/core.h"
-        "${PROJECT_SOURCE_DIR}/impl/gimli-core.h"
-        "${PROJECT_SOURCE_DIR}/impl/gimli-core/portable.h"
-        "${PROJECT_SOURCE_DIR}/impl/gimli-core/sse2.h"
-        "${PROJECT_SOURCE_DIR}/impl/hash.h"
-        "${PROJECT_SOURCE_DIR}/impl/${PROJECT_NAME}_p.h"
-        "${PROJECT_SOURCE_DIR}/impl/kdf.h"
-        "${PROJECT_SOURCE_DIR}/impl/kx.h"
-        "${PROJECT_SOURCE_DIR}/impl/pwhash.h"
-        "${PROJECT_SOURCE_DIR}/impl/random.h"
-        "${PROJECT_SOURCE_DIR}/impl/secretbox.h"
-        "${PROJECT_SOURCE_DIR}/impl/sign.h"
-        "${PROJECT_SOURCE_DIR}/impl/x25519.h")
-set(header_files
-        "${PROJECT_SOURCE_DIR}/${PROJECT_NAME}.h")
-set(test_files
-        "${PROJECT_SOURCE_DIR}/tests/tests.c")
-
-set(config_file_name "${PROJECT_NAME}-config.cmake")
-set(config_template_file "${PROJECT_SOURCE_DIR}/${config_file_name}.in")
-set(config_file "${PROJECT_BINARY_DIR}/${config_file_name}")
-
-set(targets_export_name "${PROJECT_NAME}-targets")
-set(targets_export_file_name "${targets_export_name}.cmake")
-set(targets_export_file "${PROJECT_BINARY_DIR}/${targets_export_file_name}")
+# Prefix project files with the project root
 
-set(install_config_dir "${CMAKE_INSTALL_DATADIR}/cmake/${PROJECT_NAME}")
+function(prefix_project_paths list_name)
+    list(TRANSFORM "${list_name}"
+            PREPEND "${PROJECT_SOURCE_DIR}/"
+            OUTPUT_VARIABLE prefixed_list)
+    set("project_${list_name}" ${prefixed_list} PARENT_SCOPE)
+endfunction()
 
-set(tests_executable "${PROJECT_NAME}-tests")
-set(tests_done_target "${tests_executable}-done")
-set(tests_done_file "${PROJECT_BINARY_DIR}/${tests_executable}.done")
+prefix_project_paths(source_files)
+prefix_project_paths(header_files)
+prefix_project_paths(test_files)
+prefix_project_paths(arduino_files)
 
 # Main library
 
 add_library("${PROJECT_NAME}")
 add_library("${PROJECT_NAME}::${PROJECT_NAME}" ALIAS "${PROJECT_NAME}")
 
-target_sources("${PROJECT_NAME}" PRIVATE ${source_files})
+target_sources("${PROJECT_NAME}" PRIVATE ${project_source_files})
 
 target_include_directories("${PROJECT_NAME}" PUBLIC
         $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}>
         $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>)
 
-target_compile_options(${PROJECT_NAME} PRIVATE ${compile_options})
+target_compile_options("${PROJECT_NAME}" PRIVATE ${compile_options})
 
 # Installation
 
+set(targets_export_name "${PROJECT_NAME}-targets")
+
 install(TARGETS "${PROJECT_NAME}"
         EXPORT "${targets_export_name}"
         LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}"
         ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}")
 
-install(FILES ${header_files}
+install(FILES ${project_header_files}
         DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}")
 
 # CMake find_package() support
 
+set(install_config_dir "${CMAKE_INSTALL_DATADIR}/cmake/${PROJECT_NAME}")
+
+set(targets_export_file_name "${targets_export_name}.cmake")
+set(targets_export_file "${PROJECT_BINARY_DIR}/${targets_export_file_name}")
+
 install(EXPORT "${targets_export_name}"
         FILE "${targets_export_file_name}"
         NAMESPACE "${PROJECT_NAME}::"
         DESTINATION "${install_config_dir}")
 
+set(config_file_name "${PROJECT_NAME}-config.cmake")
+set(config_template_file "${PROJECT_SOURCE_DIR}/cmake/${config_file_name}.in")
+set(config_file "${PROJECT_BINARY_DIR}/${config_file_name}")
+
 CONFIGURE_PACKAGE_CONFIG_FILE("${config_template_file}" "${config_file}"
         INSTALL_DESTINATION "${install_config_dir}")
 
-install(FILES ${config_file}
-        DESTINATION ${install_config_dir})
+install(FILES "${config_file}"
+        DESTINATION "${install_config_dir}")
 
 export(EXPORT "${targets_export_name}"
         FILE "${targets_export_file}"
@@ -110,23 +137,48 @@ export(PACKAGE "${PROJECT_NAME}")
 
 # Tests
 
+set(tests_executable "${PROJECT_NAME}-tests")
+set(tests_run_target "${PROJECT_NAME}-run-tests")
+set(tests_run_file "${PROJECT_BINARY_DIR}/${tests_run_target}.done")
+
 enable_testing()
-add_executable("${tests_executable}" ${test_files})
+add_executable("${tests_executable}" ${project_test_files})
 target_compile_options("${tests_executable}" PRIVATE ${compile_options})
 target_link_libraries("${tests_executable}" "${PROJECT_NAME}")
 add_test(NAME "${tests_executable}" COMMAND "${tests_executable}")
 
-# Auto-run tests on build (unless cross-compiling)
-
-if(NOT CMAKE_CROSSCOMPILING)
-    add_custom_command(OUTPUT "${tests_done_file}"
+if(CMAKE_CROSSCOMPILING)
+    # Disable tests executable by default when cross-compiling (as it will fail
+    # to build when, e.g., cross-compiling for Arduino/AVR).
+    set_target_properties("${tests_executable}"
+            PROPERTIES
+                EXCLUDE_FROM_ALL 1
+                EXCLUDE_FROM_DEFAULT_BUILD 1)
+else()
+    # Otherwise, auto-run the tests on build.
+    add_custom_command(OUTPUT "${tests_run_file}"
             DEPENDS "${tests_executable}"
             COMMAND cmake
-                ARGS -E remove "${tests_done_file}"
+                ARGS -E remove "${tests_run_file}"
             COMMAND ctest
                 ARGS -C $<CONFIGURATION> --output-on-failure
             COMMAND cmake
-                ARGS -E touch "${tests_done_file}"
+                ARGS -E touch "${tests_run_file}"
             WORKING_DIRECTORY "${PROJECT_BINARY_DIR}")
-    add_custom_target("${tests_done_target}" ALL DEPENDS "${tests_done_file}")
+    add_custom_target("${tests_run_target}" ALL DEPENDS "${tests_run_file}")
 endif()
+
+# Generate Arduino package
+
+set(arduino_package_file "${PROJECT_BINARY_DIR}/hydrogen-crypto.zip")
+
+# Use the relative versions of the file path lists or else the full paths will
+# end up in the generated archive.
+add_custom_command(OUTPUT "${arduino_package_file}"
+        COMMAND cmake
+            ARGS -E tar cf "${arduino_package_file}" --format=zip
+                -- ${source_files} ${header_files} ${arduino_files}
+        WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}")
+
+add_custom_target("${PROJECT_NAME}-arduino-package"
+        DEPENDS "${arduino_package_file}")

+ 126 - 0
cmake/arduino-avr-toolchain.cmake

@@ -0,0 +1,126 @@
+# Cross-compilation file for the Arduino/AVR toolchain.
+
+# To use, pass -DCMAKE_TOOLCHAIN_FILE=cmake/arduino-avr-toolchain.cmake in your
+# CMake command line.
+
+cmake_minimum_required(VERSION 3.12)
+
+set(setting_prefix ARDUINO_AVR)
+function(get_setting setting_name setting_type setting_description)
+    string(TOUPPER "${setting_prefix}_${setting_name}" setting_external_name)
+    set("${setting_external_name}" "" CACHE "${setting_type}" "${setting_description}")
+    set("${setting_name}" "${${setting_external_name}}" PARENT_SCOPE)
+endfunction()
+
+# Target device setting
+
+get_setting(target_device STRING "Target Arduino device MCU identifier.")
+if(NOT target_device)
+    set(target_device atmega328p)
+endif()
+
+if("${target_device}" STREQUAL atmega328p)
+    set(hw_type ATMEGA328)
+else()
+    message(FATAL_ERROR "Unrecognized ${setting_prefix}_TARGET_DEVICE value ${target_device}")
+endif()
+
+# Find Arduino SDK home
+
+get_setting(sdk_dir PATH "Arduino SDK home directory.")
+
+# Try ARDUINO_SDK_PATH environment variable.
+if(NOT sdk_dir)
+    if(DEFINED ENV{ARDUINO_SDK_PATH})
+        set(sdk_dir "$ENV{ARDUINO_SDK_PATH}")
+    endif()
+endif()
+
+# Try some platform-specific guesses.
+if(NOT sdk_dir)
+    # Windows
+    if(WIN32)
+        list(APPEND arduino_home_dir_guesses
+                "C:/Program Files (x86)/Arduino"
+                "C:/Program Files/Arduino")
+    endif()
+
+    # macOS
+    if(APPLE)
+        list(APPEND arduino_home_dir_guesses
+                "/Applications/Arduino.app/Contents/Java")
+    endif()
+
+    # Linux/Unix
+    if(UNIX AND NOT APPLE)
+        list(APPEND arduino_home_dir_guesses
+                "/usr/share/arduino"
+                "/usr/local/share/arduino")
+    endif()
+
+    if(DEFINED arduino_home_dir_guesses)
+        foreach(arduino_home_dir_guess IN LISTS arduino_home_dir_guesses)
+            if(IS_DIRECTORY "${arduino_home_dir_guess}")
+                set(sdk_dir "${arduino_home_dir_guess}")
+                break()
+            endif()
+        endforeach()
+    endif()
+endif()
+
+if(NOT sdk_dir)
+    message(FATAL_ERROR
+            "Couldn't determine Arduino SDK home directory. "
+            "Try passing -D${setting_prefix}_SDK_DIR=... to the CMake command line, or "
+            "set the ARDUINO_SDK_PATH environment variable.")
+endif()
+
+# Locate toolchain programs
+
+set(arduino_tools_dir "${sdk_dir}/hardware/tools/avr/bin")
+set(program_prefix "${setting_prefix}_PROGRAM")
+function(find_in_toolchain program_name)
+    string(TOUPPER "${program_prefix}_${program_name}" program_external_name)
+    string(REPLACE "_" "-" program_file_name "${program_name}")
+
+    find_program("${program_external_name}" "${program_file_name}"
+            PATHS "${arduino_tools_dir}" NO_DEFAULT_PATH)
+
+    if("${${program_external_name}}" STREQUAL "${program_external_name}-NOTFOUND")
+        message(FATAL_ERROR
+                "Couldn't find program ${program_file_name} "
+                "in Arduino/AVR toolchain at ${arduino_tools_dir}")
+    else()
+        set("${program_name}" "${${program_external_name}}" PARENT_SCOPE)
+    endif()
+endfunction()
+
+find_in_toolchain(avr_gcc)
+find_in_toolchain(avr_gcc_ranlib)
+find_in_toolchain(avr_gcc_ar)
+
+# Configure CMake toolchain settings
+
+set(CMAKE_SYSTEM_NAME Generic)
+set(CMAKE_C_COMPILER "${avr_gcc}")
+set(CMAKE_ASM_COMPILER "${avr_gcc}")
+set(CMAKE_RANLIB "${avr_gcc_ranlib}")
+set(CMAKE_AR "${avr_gcc_ar}")
+
+SET(CMAKE_C_OUTPUT_EXTENSION .o)
+SET(CMAKE_ASM_OUTPUT_EXTENSION .o)
+
+# Add compile flags
+
+string(APPEND CMAKE_C_FLAGS
+        " -mmcu=${target_device} -Os -mcall-prologues -fno-exceptions"
+        " -ffunction-sections -fdata-sections -flto"
+        " -DHYDRO_TARGET_DEVICE_${hw_type}")
+
+# Add include directories
+
+include_directories(SYSTEM
+        "${sdk_dir}/hardware/arduino/avr/cores/arduino"
+        "${sdk_dir}/hardware/arduino/avr/variants/standard"
+        "${sdk_dir}/hardware/arduino/cores/arduino"
+        "${sdk_dir}/hardware/arduino/variants/standard")

+ 0 - 0
hydrogen-config.cmake.in → cmake/hydrogen-config.cmake.in