Prepare parts of the build system
extracted and slightly modified from Buddy-FW, still doesn't workvintagepc/version-and-build
parent
3f7e32f6cf
commit
257d0ec340
|
|
@ -0,0 +1,126 @@
|
||||||
|
---
|
||||||
|
Language: Cpp
|
||||||
|
# BasedOnStyle: WebKit
|
||||||
|
AccessModifierOffset: -4
|
||||||
|
AlignAfterOpenBracket: DontAlign
|
||||||
|
AlignConsecutiveMacros: true
|
||||||
|
AlignConsecutiveAssignments: false
|
||||||
|
AlignConsecutiveDeclarations: false
|
||||||
|
AlignEscapedNewlines: Right
|
||||||
|
AlignOperands: false
|
||||||
|
AlignTrailingComments: true
|
||||||
|
AllowAllArgumentsOnNextLine: true
|
||||||
|
AllowAllConstructorInitializersOnNextLine: true
|
||||||
|
AllowAllParametersOfDeclarationOnNextLine: true
|
||||||
|
AllowShortBlocksOnASingleLine: false
|
||||||
|
AllowShortCaseLabelsOnASingleLine: false
|
||||||
|
AllowShortFunctionsOnASingleLine: All
|
||||||
|
AllowShortLambdasOnASingleLine: All
|
||||||
|
AllowShortIfStatementsOnASingleLine: Never
|
||||||
|
AllowShortLoopsOnASingleLine: false
|
||||||
|
AlwaysBreakAfterDefinitionReturnType: None
|
||||||
|
AlwaysBreakAfterReturnType: None
|
||||||
|
AlwaysBreakBeforeMultilineStrings: false
|
||||||
|
AlwaysBreakTemplateDeclarations: MultiLine
|
||||||
|
BinPackArguments: true
|
||||||
|
BinPackParameters: true
|
||||||
|
BraceWrapping:
|
||||||
|
AfterCaseLabel: false
|
||||||
|
AfterClass: false
|
||||||
|
AfterControlStatement: false
|
||||||
|
AfterEnum: false
|
||||||
|
AfterFunction: false
|
||||||
|
AfterNamespace: false
|
||||||
|
AfterObjCDeclaration: false
|
||||||
|
AfterStruct: false
|
||||||
|
AfterUnion: false
|
||||||
|
AfterExternBlock: false
|
||||||
|
BeforeCatch: false
|
||||||
|
BeforeElse: false
|
||||||
|
IndentBraces: false
|
||||||
|
SplitEmptyFunction: true
|
||||||
|
SplitEmptyRecord: true
|
||||||
|
SplitEmptyNamespace: true
|
||||||
|
BreakBeforeBinaryOperators: All
|
||||||
|
BreakBeforeBraces: Custom
|
||||||
|
BreakBeforeInheritanceComma: false
|
||||||
|
BreakInheritanceList: BeforeColon
|
||||||
|
BreakBeforeTernaryOperators: true
|
||||||
|
BreakConstructorInitializersBeforeComma: false
|
||||||
|
BreakConstructorInitializers: BeforeComma
|
||||||
|
BreakAfterJavaFieldAnnotations: false
|
||||||
|
BreakStringLiterals: true
|
||||||
|
ColumnLimit: 0
|
||||||
|
CommentPragmas: '^ IWYU pragma:'
|
||||||
|
CompactNamespaces: false
|
||||||
|
ConstructorInitializerAllOnOneLineOrOnePerLine: false
|
||||||
|
ConstructorInitializerIndentWidth: 4
|
||||||
|
ContinuationIndentWidth: 4
|
||||||
|
Cpp11BracedListStyle: false
|
||||||
|
DerivePointerAlignment: false
|
||||||
|
DisableFormat: false
|
||||||
|
ExperimentalAutoDetectBinPacking: false
|
||||||
|
FixNamespaceComments: false
|
||||||
|
ForEachMacros:
|
||||||
|
- foreach
|
||||||
|
- Q_FOREACH
|
||||||
|
- BOOST_FOREACH
|
||||||
|
IncludeBlocks: Preserve
|
||||||
|
IncludeCategories:
|
||||||
|
- Regex: '^"(llvm|llvm-c|clang|clang-c)/'
|
||||||
|
Priority: 2
|
||||||
|
- Regex: '^(<|"(gtest|gmock|isl|json)/)'
|
||||||
|
Priority: 3
|
||||||
|
- Regex: '.*'
|
||||||
|
Priority: 1
|
||||||
|
IncludeIsMainRegex: '(Test)?$'
|
||||||
|
IndentCaseLabels: false
|
||||||
|
IndentPPDirectives: BeforeHash
|
||||||
|
IndentWidth: 4
|
||||||
|
IndentWrappedFunctionNames: false
|
||||||
|
JavaScriptQuotes: Leave
|
||||||
|
JavaScriptWrapImports: true
|
||||||
|
KeepEmptyLinesAtTheStartOfBlocks: true
|
||||||
|
MacroBlockBegin: ''
|
||||||
|
MacroBlockEnd: ''
|
||||||
|
MaxEmptyLinesToKeep: 1
|
||||||
|
NamespaceIndentation: Inner
|
||||||
|
ObjCBinPackProtocolList: Auto
|
||||||
|
ObjCBlockIndentWidth: 4
|
||||||
|
ObjCSpaceAfterProperty: true
|
||||||
|
ObjCSpaceBeforeProtocolList: true
|
||||||
|
PenaltyBreakAssignment: 2
|
||||||
|
PenaltyBreakBeforeFirstCallParameter: 19
|
||||||
|
PenaltyBreakComment: 300
|
||||||
|
PenaltyBreakFirstLessLess: 120
|
||||||
|
PenaltyBreakString: 1000
|
||||||
|
PenaltyBreakTemplateDeclaration: 10
|
||||||
|
PenaltyExcessCharacter: 1000000
|
||||||
|
PenaltyReturnTypeOnItsOwnLine: 60
|
||||||
|
PointerAlignment: Right
|
||||||
|
ReflowComments: true
|
||||||
|
SortIncludes: false
|
||||||
|
SortUsingDeclarations: true
|
||||||
|
SpaceAfterCStyleCast: false
|
||||||
|
SpaceAfterLogicalNot: false
|
||||||
|
SpaceAfterTemplateKeyword: true
|
||||||
|
SpaceBeforeAssignmentOperators: true
|
||||||
|
SpaceBeforeCpp11BracedList: true
|
||||||
|
SpaceBeforeCtorInitializerColon: true
|
||||||
|
SpaceBeforeInheritanceColon: true
|
||||||
|
SpaceBeforeParens: ControlStatements
|
||||||
|
SpaceBeforeRangeBasedForLoopColon: true
|
||||||
|
SpaceInEmptyParentheses: false
|
||||||
|
SpacesBeforeTrailingComments: 1
|
||||||
|
SpacesInAngles: false
|
||||||
|
SpacesInContainerLiterals: true
|
||||||
|
SpacesInCStyleCastParentheses: false
|
||||||
|
SpacesInParentheses: false
|
||||||
|
SpacesInSquareBrackets: false
|
||||||
|
Standard: Cpp11
|
||||||
|
StatementMacros:
|
||||||
|
- Q_UNUSED
|
||||||
|
- QT_REQUIRE_VERSION
|
||||||
|
TabWidth: 8
|
||||||
|
UseTab: Never
|
||||||
|
...
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
# If a statement is wrapped to more than one line, than dangle the closing
|
||||||
|
# parenthesis on it's own line.
|
||||||
|
dangle_parens = True
|
||||||
|
dangle_align = 'child'
|
||||||
|
|
||||||
|
# If true, the parsers may infer whether or not an argument list is sortable
|
||||||
|
# (without annotation).
|
||||||
|
autosort = True
|
||||||
|
|
||||||
|
# How wide to allow formatted cmake files
|
||||||
|
line_width = 100
|
||||||
|
|
||||||
|
additional_commands = {
|
||||||
|
"target_sources": {
|
||||||
|
"kwargs": {
|
||||||
|
"PUBLIC": "*",
|
||||||
|
"PRIVATE": "*",
|
||||||
|
"INTERFACE": "*",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
.vs
|
||||||
|
/build*
|
||||||
|
.cproject
|
||||||
|
.project
|
||||||
|
.settings
|
||||||
|
.dependencies
|
||||||
|
.DS_Store
|
||||||
|
/CMakeLists.txt.user
|
||||||
|
.ccls-cache
|
||||||
|
.idea
|
||||||
|
compile_commands.json
|
||||||
|
/.vscode/*.peripherals.state.json
|
||||||
|
/.vscode/*.registers.state.json
|
||||||
|
Makefile
|
||||||
|
/doc/html/
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
# See https://pre-commit.com for more information
|
||||||
|
# See https://pre-commit.com/hooks.html for more hooks
|
||||||
|
repos:
|
||||||
|
- repo: https://github.com/dragomirecky/cmake-format-pre-commit-hook
|
||||||
|
rev: 'v0.6.0'
|
||||||
|
hooks:
|
||||||
|
- id: cmake-format # cmake formatter
|
||||||
|
files: (CMakeLists.*|.*\.cmake|.*\.cmake.in)
|
||||||
|
- repo: https://github.com/pre-commit/mirrors-yapf
|
||||||
|
rev: 'v0.27.0'
|
||||||
|
hooks:
|
||||||
|
- id: yapf # python formatter
|
||||||
|
- repo: local
|
||||||
|
hooks:
|
||||||
|
- id: clang-format
|
||||||
|
name: clang-format
|
||||||
|
description: This hook automatically checks and reformats changed files using clang-format formatter.
|
||||||
|
entry: './.dependencies/clang-format-9.0.0-noext/clang-format'
|
||||||
|
language: script
|
||||||
|
files: \.(h\+\+|h|hh|hxx|hpp|cuh|c|cc|cpp|cu|c\+\+|cxx|tpp|txx)$
|
||||||
|
args: ['-i', '-style=file']
|
||||||
|
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||||
|
rev: 'v2.4.0'
|
||||||
|
hooks:
|
||||||
|
- id: trailing-whitespace
|
||||||
|
args: [--markdown-linebreak-ext=md]
|
||||||
|
- id: end-of-file-fixer
|
||||||
|
- id: mixed-line-ending
|
||||||
|
exclude: |
|
||||||
|
(?x)(
|
||||||
|
^tests/unit/lang/translator/(keys|cs|es|fr|de|pl|it)\.txt$|
|
||||||
|
^lib/inih/|
|
||||||
|
^lib/Marlin/|
|
||||||
|
^lib/Prusa-Error-Codes/|
|
||||||
|
^lib/TMCStepper/|
|
||||||
|
^lib/Middlewares/Third_Party/LwIP/|
|
||||||
|
^lib/jsmn/|
|
||||||
|
^lib/Catch2/
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,199 @@
|
||||||
|
cmake_minimum_required(VERSION 3.15)
|
||||||
|
include(cmake/Utilities.cmake)
|
||||||
|
include(cmake/GetGitRevisionDescription.cmake)
|
||||||
|
include(cmake/ProjectVersion.cmake)
|
||||||
|
|
||||||
|
project(
|
||||||
|
MMU
|
||||||
|
LANGUAGES C CXX ASM
|
||||||
|
VERSION ${PROJECT_VERSION}
|
||||||
|
)
|
||||||
|
|
||||||
|
if(NOT CMAKE_CROSSCOMPILING)
|
||||||
|
#
|
||||||
|
# If we are not crosscompiling, include `utils` with host tools.
|
||||||
|
#
|
||||||
|
add_subdirectory(utils)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
#
|
||||||
|
# Command Line Options
|
||||||
|
#
|
||||||
|
# You should specify those options when invoking CMake. Example:
|
||||||
|
# ~~~
|
||||||
|
# cmake .. <other options> -DPRINTER=MMU
|
||||||
|
# ~~~
|
||||||
|
|
||||||
|
set(PRINTER_VALID_OPTS "MMU")
|
||||||
|
|
||||||
|
set(PRINTER
|
||||||
|
"MMU"
|
||||||
|
CACHE
|
||||||
|
STRING
|
||||||
|
"Select the MMU unit for which you want to compile the project (valid values are ${PRINTER_VALID_OPTS})."
|
||||||
|
)
|
||||||
|
set(PROJECT_VERSION_SUFFIX
|
||||||
|
"<auto>"
|
||||||
|
CACHE
|
||||||
|
STRING
|
||||||
|
"Full version suffix to be shown on the info screen in settings (e.g. full_version=4.0.3-BETA+1035.PR111.B4, suffix=-BETA+1035.PR111.B4). Defaults to '+<commit sha>.<dirty?>.<debug?>' if set to '<auto>'."
|
||||||
|
)
|
||||||
|
set(PROJECT_VERSION_SUFFIX_SHORT
|
||||||
|
"<auto>"
|
||||||
|
CACHE
|
||||||
|
STRING
|
||||||
|
"Short version suffix to be shown on splash screen. Defaults to '+<BUILD_NUMBER>' if set to '<auto>'."
|
||||||
|
)
|
||||||
|
set(BUILD_NUMBER
|
||||||
|
""
|
||||||
|
CACHE STRING "Build number of the firmware. Resolved automatically if not specified."
|
||||||
|
)
|
||||||
|
set(CUSTOM_COMPILE_OPTIONS
|
||||||
|
""
|
||||||
|
CACHE STRING "Allows adding custom C/C++ flags"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Validate options
|
||||||
|
foreach(OPTION "PRINTER")
|
||||||
|
if(NOT ${OPTION} IN_LIST ${OPTION}_VALID_OPTS)
|
||||||
|
message(FATAL_ERROR "Invalid ${OPTION} ${${OPTION}}: Valid values are ${${OPTION}_VALID_OPTS}")
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
# Resolve BUILD_NUMBER and PROJECT_VERSION_* variables
|
||||||
|
resolve_version_variables()
|
||||||
|
|
||||||
|
# Check GCC Version
|
||||||
|
get_recommended_gcc_version(RECOMMENDED_TOOLCHAIN_VERSION)
|
||||||
|
if(CMAKE_CROSSCOMPILING AND NOT CMAKE_CXX_COMPILER_VERSION VERSION_EQUAL
|
||||||
|
${RECOMMENDED_TOOLCHAIN_VERSION}
|
||||||
|
)
|
||||||
|
message(WARNING "Recommended AVR toolchain is ${RECOMMENDED_TOOLCHAIN_VERSION}"
|
||||||
|
", but you have ${CMAKE_CXX_COMPILER_VERSION}"
|
||||||
|
)
|
||||||
|
|
||||||
|
elseif(NOT CMAKE_CROSSCOMPILING AND NOT CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
||||||
|
message(
|
||||||
|
WARNING
|
||||||
|
"Recommended compiler for host tools and unittests is GCC, you have ${CMAKE_CXX_COMPILER_ID}."
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Inform user about the resolved settings
|
||||||
|
message(STATUS "Project version: ${PROJECT_VERSION}")
|
||||||
|
message(STATUS "Project version with full suffix: ${PROJECT_VERSION_FULL}")
|
||||||
|
message(
|
||||||
|
STATUS "Project version with short suffix: ${PROJECT_VERSION}${PROJECT_VERSION_SUFFIX_SHORT}"
|
||||||
|
)
|
||||||
|
message(STATUS "Using toolchain file: ${CMAKE_TOOLCHAIN_FILE}.")
|
||||||
|
message(STATUS "Printer: ${PRINTER}")
|
||||||
|
|
||||||
|
# eclipse sets those variables, so lets just use them so we don't get a warning about unused
|
||||||
|
# variables
|
||||||
|
set(unused "${CMAKE_VERBOSE_MAKEFILE} ${CMAKE_RULE_MESSAGES}")
|
||||||
|
|
||||||
|
# append custom C/C++ flags
|
||||||
|
if(CUSTOM_COMPILE_OPTIONS)
|
||||||
|
string(REPLACE " " ";" CUSTOM_COMPILE_OPTIONS "${CUSTOM_COMPILE_OPTIONS}")
|
||||||
|
add_compile_options(${CUSTOM_COMPILE_OPTIONS})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
#
|
||||||
|
# MMUHeaders
|
||||||
|
#
|
||||||
|
|
||||||
|
# add_library(MMUHeaders INTERFACE) target_include_directories( MMUHeaders INTERFACE include
|
||||||
|
# include/stm32f4_hal include/usb_host include/usb_device include/marlin include/freertos )
|
||||||
|
|
||||||
|
# target_link_libraries(A3idesHeaders INTERFACE STM32F4::HAL FreeRTOS::FreeRTOS)
|
||||||
|
|
||||||
|
# target_compile_definitions( A3idesHeaders INTERFACE PRINTER_TYPE=PRINTER_PRUSA_${PRINTER} )
|
||||||
|
|
||||||
|
#
|
||||||
|
# Global Compiler & Linker Configuration
|
||||||
|
#
|
||||||
|
|
||||||
|
# include symbols
|
||||||
|
add_compile_options(-g)
|
||||||
|
|
||||||
|
# optimizations
|
||||||
|
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||||
|
add_compile_options(-Og)
|
||||||
|
else()
|
||||||
|
add_compile_options(-Os)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(CMAKE_CROSSCOMPILING)
|
||||||
|
# mcu related settings set(MCU_FLAGS -mthumb -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=fpv4-sp-d16)
|
||||||
|
# add_compile_options(${MCU_FLAGS}) add_link_options(${MCU_FLAGS})
|
||||||
|
|
||||||
|
# split and gc sections
|
||||||
|
add_compile_options(-ffunction-sections -fdata-sections)
|
||||||
|
add_link_options(-Wl,--gc-sections)
|
||||||
|
|
||||||
|
# disable exceptions and related metadata
|
||||||
|
add_compile_options(-fno-exceptions -fno-unwind-tables)
|
||||||
|
add_link_options(-Wl,--defsym,__exidx_start=0,--defsym,__exidx_end=0)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# enable all warnings (well, not all, but some)
|
||||||
|
add_compile_options(-Wall -Wsign-compare)
|
||||||
|
add_compile_options($<$<COMPILE_LANGUAGE:CXX>:-Wno-register> $<$<COMPILE_LANGUAGE:CXX>:-std=c++14>)
|
||||||
|
|
||||||
|
# support _DEBUG macro (some code uses to recognize debug builds)
|
||||||
|
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||||
|
add_compile_definitions(_DEBUG)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# if(CMAKE_CROSSCOMPILING) # configure linker script set(LINKER_SCRIPT
|
||||||
|
# "${CMAKE_CURRENT_SOURCE_DIR}/src/STM32F407VG_FLASH.ld")
|
||||||
|
# add_link_options("-Wl,-T,${LINKER_SCRIPT}") endif()
|
||||||
|
|
||||||
|
#
|
||||||
|
# Import definitions of all libraries
|
||||||
|
#
|
||||||
|
|
||||||
|
# add_subdirectory(lib)
|
||||||
|
|
||||||
|
#
|
||||||
|
# MMU firmware
|
||||||
|
#
|
||||||
|
|
||||||
|
add_executable(firmware)
|
||||||
|
|
||||||
|
set_target_properties(firmware PROPERTIES CXX_STANDARD 14)
|
||||||
|
|
||||||
|
# generate firmware.bin file
|
||||||
|
objcopy(firmware "binary" ".bin")
|
||||||
|
|
||||||
|
# generate linker map file
|
||||||
|
target_link_options(firmware PUBLIC -Wl,-Map=firmware.map)
|
||||||
|
|
||||||
|
# inform about the firmware's size in terminal
|
||||||
|
report_size(firmware)
|
||||||
|
|
||||||
|
# add_link_dependency(firmware "${LINKER_SCRIPT}")
|
||||||
|
|
||||||
|
target_include_directories(firmware PRIVATE include src)
|
||||||
|
|
||||||
|
target_compile_options(firmware PRIVATE -Wdouble-promotion)
|
||||||
|
|
||||||
|
# target_link_libraries( firmware PRIVATE A3idesHeaders )
|
||||||
|
|
||||||
|
target_sources(firmware PRIVATE src/main.cpp)
|
||||||
|
|
||||||
|
set_property(
|
||||||
|
SOURCE src/version.c
|
||||||
|
APPEND
|
||||||
|
PROPERTY COMPILE_DEFINITIONS
|
||||||
|
FW_BUILD_NUMBER=${BUILD_NUMBER}
|
||||||
|
FW_VERSION_FULL=${PROJECT_VERSION_FULL}
|
||||||
|
FW_VERSION=${PROJECT_VERSION}
|
||||||
|
FW_VERSION_SUFFIX=${PROJECT_VERSION_SUFFIX}
|
||||||
|
FW_VERSION_SUFFIX_SHORT=${PROJECT_VERSION_SUFFIX_SHORT}
|
||||||
|
)
|
||||||
|
|
||||||
|
if(NOT CMAKE_CROSSCOMPILING)
|
||||||
|
enable_testing()
|
||||||
|
add_subdirectory(tests)
|
||||||
|
endif()
|
||||||
|
|
@ -0,0 +1,99 @@
|
||||||
|
get_filename_component(PROJECT_CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" DIRECTORY)
|
||||||
|
include("${PROJECT_CMAKE_DIR}/Utilities.cmake")
|
||||||
|
set(CMAKE_SYSTEM_NAME Generic)
|
||||||
|
set(CMAKE_SYSTEM_PROCESSOR ARM)
|
||||||
|
|
||||||
|
#
|
||||||
|
# Utilities
|
||||||
|
|
||||||
|
if(MINGW
|
||||||
|
OR CYGWIN
|
||||||
|
OR WIN32
|
||||||
|
)
|
||||||
|
set(UTIL_SEARCH_CMD where)
|
||||||
|
set(EXECUTABLE_SUFFIX ".exe")
|
||||||
|
elseif(UNIX OR APPLE)
|
||||||
|
set(UTIL_SEARCH_CMD which)
|
||||||
|
set(EXECUTABLE_SUFFIX "")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(TOOLCHAIN_PREFIX arm-none-eabi-)
|
||||||
|
|
||||||
|
#
|
||||||
|
# Looking up the toolchain
|
||||||
|
#
|
||||||
|
|
||||||
|
if(ARM_TOOLCHAIN_DIR)
|
||||||
|
# using toolchain set by gcc-arm-none-eabi.cmake (locked version)
|
||||||
|
set(BINUTILS_PATH "${ARM_TOOLCHAIN_DIR}/bin")
|
||||||
|
else()
|
||||||
|
# search for ANY arm-none-eabi-gcc toolchain
|
||||||
|
execute_process(
|
||||||
|
COMMAND ${UTIL_SEARCH_CMD} ${TOOLCHAIN_PREFIX}gcc
|
||||||
|
OUTPUT_VARIABLE ARM_NONE_EABI_GCC_PATH
|
||||||
|
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||||
|
RESULT_VARIABLE FIND_RESULT
|
||||||
|
)
|
||||||
|
# found?
|
||||||
|
if(NOT "${FIND_RESULT}" STREQUAL "0")
|
||||||
|
message(FATAL_ERROR "arm-none-eabi-gcc not found")
|
||||||
|
endif()
|
||||||
|
get_filename_component(BINUTILS_PATH "${ARM_NONE_EABI_GCC_PATH}" DIRECTORY)
|
||||||
|
get_filename_component(ARM_TOOLCHAIN_DIR ${BINUTILS_PATH} DIRECTORY)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
#
|
||||||
|
# Setup CMake
|
||||||
|
#
|
||||||
|
|
||||||
|
# Without that flag CMake is not able to pass test compilation check
|
||||||
|
set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)
|
||||||
|
|
||||||
|
set(triple armv7m-none-eabi)
|
||||||
|
set(CMAKE_C_COMPILER
|
||||||
|
"${BINUTILS_PATH}/${TOOLCHAIN_PREFIX}gcc${EXECUTABLE_SUFFIX}"
|
||||||
|
CACHE FILEPATH "" FORCE
|
||||||
|
)
|
||||||
|
set(CMAKE_C_COMPILER_TARGET
|
||||||
|
${triple}
|
||||||
|
CACHE STRING "" FORCE
|
||||||
|
)
|
||||||
|
set(CMAKE_ASM_COMPILER
|
||||||
|
"${BINUTILS_PATH}/${TOOLCHAIN_PREFIX}gcc${EXECUTABLE_SUFFIX}"
|
||||||
|
CACHE FILEPATH "" FORCE
|
||||||
|
)
|
||||||
|
set(CMAKE_ASM_COMPILER_TARGET
|
||||||
|
${triple}
|
||||||
|
CACHE STRING "" FORCE
|
||||||
|
)
|
||||||
|
set(CMAKE_CXX_COMPILER
|
||||||
|
"${BINUTILS_PATH}/${TOOLCHAIN_PREFIX}g++${EXECUTABLE_SUFFIX}"
|
||||||
|
CACHE FILEPATH "" FORCE
|
||||||
|
)
|
||||||
|
set(CMAKE_CXX_COMPILER_TARGET
|
||||||
|
${triple}
|
||||||
|
CACHE STRING "" FORCE
|
||||||
|
)
|
||||||
|
set(CMAKE_EXE_LINKER_FLAGS_INIT
|
||||||
|
"--specs=nosys.specs"
|
||||||
|
CACHE STRING "" FORCE
|
||||||
|
)
|
||||||
|
|
||||||
|
set(CMAKE_ASM_COMPILE_OBJECT
|
||||||
|
"<CMAKE_ASM_COMPILER> <DEFINES> <FLAGS> -o <OBJECT> -c <SOURCE>"
|
||||||
|
CACHE STRING "" FORCE
|
||||||
|
)
|
||||||
|
|
||||||
|
set(CMAKE_OBJCOPY
|
||||||
|
"${BINUTILS_PATH}/${TOOLCHAIN_PREFIX}objcopy${EXECUTABLE_SUFFIX}"
|
||||||
|
CACHE INTERNAL "objcopy tool"
|
||||||
|
)
|
||||||
|
set(CMAKE_SIZE_UTIL
|
||||||
|
"${BINUTILS_PATH}/${TOOLCHAIN_PREFIX}size${EXECUTABLE_SUFFIX}"
|
||||||
|
CACHE INTERNAL "size tool"
|
||||||
|
)
|
||||||
|
|
||||||
|
set(CMAKE_FIND_ROOT_PATH "${ARM_TOOLCHAIN_DIR}")
|
||||||
|
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
|
||||||
|
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
|
||||||
|
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
# getlocked version
|
||||||
|
get_filename_component(PROJECT_CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" DIRECTORY)
|
||||||
|
get_filename_component(PROJECT_ROOT_DIR "${PROJECT_CMAKE_DIR}" DIRECTORY)
|
||||||
|
include("${PROJECT_CMAKE_DIR}/Utilities.cmake")
|
||||||
|
|
||||||
|
get_recommended_gcc_version(RECOMMENDED_TOOLCHAIN_VERSION)
|
||||||
|
set(RECOMMENDED_TOOLCHAIN_BINUTILS
|
||||||
|
"${PROJECT_ROOT_DIR}/.dependencies/gcc-arm-none-eabi-${RECOMMENDED_TOOLCHAIN_VERSION}/bin"
|
||||||
|
)
|
||||||
|
|
||||||
|
# check that the locked version of gcc-arm-none-eabi is present
|
||||||
|
if(NOT EXISTS "${RECOMMENDED_TOOLCHAIN_BINUTILS}")
|
||||||
|
message(
|
||||||
|
FATAL_ERROR
|
||||||
|
"arm-none-eabi-gcc (version ${RECOMMENDED_TOOLCHAIN_VERSION}) not found. Run the command below to download it.\n"
|
||||||
|
"${PROJECT_ROOT_DIR}/utils/bootstrap.sh\n"
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# include any-gcc-arm-none-eabi toolchain and pass in ARM_TOOLCHAIN_DIR
|
||||||
|
get_filename_component(ARM_TOOLCHAIN_DIR "${RECOMMENDED_TOOLCHAIN_BINUTILS}" DIRECTORY)
|
||||||
|
include("${PROJECT_ROOT_DIR}/cmake/AnyGccArmNoneEabi.cmake")
|
||||||
|
|
@ -0,0 +1,232 @@
|
||||||
|
# * Returns a version string from Git
|
||||||
|
#
|
||||||
|
# These functions force a re-configure on each git commit so that you can trust the values of the
|
||||||
|
# variables in your build system.
|
||||||
|
#
|
||||||
|
# get_git_head_revision(<refspecvar> <hashvar> [<additional arguments to git describe> ...])
|
||||||
|
#
|
||||||
|
# Returns the refspec and sha hash of the current head revision
|
||||||
|
#
|
||||||
|
# git_describe(<var> [<additional arguments to git describe> ...])
|
||||||
|
#
|
||||||
|
# Returns the results of git describe on the source tree, and adjusting the output so that it tests
|
||||||
|
# false if an error occurs.
|
||||||
|
#
|
||||||
|
# git_get_exact_tag(<var> [<additional arguments to git describe> ...])
|
||||||
|
#
|
||||||
|
# Returns the results of git describe --exact-match on the source tree, and adjusting the output so
|
||||||
|
# that it tests false if there was no exact matching tag.
|
||||||
|
#
|
||||||
|
# git_local_changes(<var>)
|
||||||
|
#
|
||||||
|
# Returns either "CLEAN" or "DIRTY" with respect to uncommitted changes. Uses the return code of
|
||||||
|
# "git diff-index --quiet HEAD --". Does not regard untracked files.
|
||||||
|
#
|
||||||
|
# git_count_parent_commits(<var>)
|
||||||
|
#
|
||||||
|
# Returns number of commits preceeding current commit -1 if git rev-list --count HEAD failed or
|
||||||
|
# "GIT-NOTFOUND" if git executable was not found or "HEAD-HASH-NOTFOUND" if head hash was not found.
|
||||||
|
# I don't know if get_git_head_revision() must be called internally or not, as reason of calling it
|
||||||
|
# is not clear for me also in git_local_changes().
|
||||||
|
#
|
||||||
|
# Requires CMake 2.6 or newer (uses the 'function' command)
|
||||||
|
#
|
||||||
|
# Original Author: 2009-2010 Ryan Pavlik <rpavlik@iastate.edu> <abiryan@ryand.net>
|
||||||
|
# http://academic.cleardefinition.com Iowa State University HCI Graduate Program/VRAC
|
||||||
|
#
|
||||||
|
# Copyright Iowa State University 2009-2010. Distributed under the Boost Software License, Version
|
||||||
|
# 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
|
||||||
|
if(__get_git_revision_description)
|
||||||
|
return()
|
||||||
|
endif()
|
||||||
|
set(__get_git_revision_description YES)
|
||||||
|
|
||||||
|
# We must run the following at "include" time, not at function call time, to find the path to this
|
||||||
|
# module rather than the path to a calling list file
|
||||||
|
get_filename_component(_gitdescmoddir ${CMAKE_CURRENT_LIST_FILE} PATH)
|
||||||
|
|
||||||
|
function(get_git_head_revision _refspecvar _hashvar)
|
||||||
|
set(GIT_PARENT_DIR "${CMAKE_CURRENT_SOURCE_DIR}")
|
||||||
|
set(GIT_DIR "${GIT_PARENT_DIR}/.git")
|
||||||
|
while(NOT EXISTS "${GIT_DIR}") # .git dir not found, search parent directories
|
||||||
|
set(GIT_PREVIOUS_PARENT "${GIT_PARENT_DIR}")
|
||||||
|
get_filename_component(GIT_PARENT_DIR ${GIT_PARENT_DIR} PATH)
|
||||||
|
if(GIT_PARENT_DIR STREQUAL GIT_PREVIOUS_PARENT)
|
||||||
|
# We have reached the root directory, we are not in git
|
||||||
|
set(${_refspecvar}
|
||||||
|
"GITDIR-NOTFOUND"
|
||||||
|
PARENT_SCOPE
|
||||||
|
)
|
||||||
|
set(${_hashvar}
|
||||||
|
"GITDIR-NOTFOUND"
|
||||||
|
PARENT_SCOPE
|
||||||
|
)
|
||||||
|
return()
|
||||||
|
endif()
|
||||||
|
set(GIT_DIR "${GIT_PARENT_DIR}/.git")
|
||||||
|
endwhile()
|
||||||
|
# check if this is a submodule
|
||||||
|
if(NOT IS_DIRECTORY ${GIT_DIR})
|
||||||
|
file(READ ${GIT_DIR} submodule)
|
||||||
|
string(REGEX REPLACE "gitdir: (.*)\n$" "\\1" GIT_DIR_RELATIVE ${submodule})
|
||||||
|
get_filename_component(SUBMODULE_DIR ${GIT_DIR} PATH)
|
||||||
|
get_filename_component(GIT_DIR ${SUBMODULE_DIR}/${GIT_DIR_RELATIVE} ABSOLUTE)
|
||||||
|
endif()
|
||||||
|
set(GIT_DATA "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/git-data")
|
||||||
|
if(NOT EXISTS "${GIT_DATA}")
|
||||||
|
file(MAKE_DIRECTORY "${GIT_DATA}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(NOT EXISTS "${GIT_DIR}/HEAD")
|
||||||
|
return()
|
||||||
|
endif()
|
||||||
|
set(HEAD_FILE "${GIT_DATA}/HEAD")
|
||||||
|
configure_file("${GIT_DIR}/HEAD" "${HEAD_FILE}" COPYONLY)
|
||||||
|
|
||||||
|
configure_file(
|
||||||
|
"${_gitdescmoddir}/GetGitRevisionDescription.cmake.in" "${GIT_DATA}/grabRef.cmake" @ONLY
|
||||||
|
)
|
||||||
|
include("${GIT_DATA}/grabRef.cmake")
|
||||||
|
|
||||||
|
set(${_refspecvar}
|
||||||
|
"${HEAD_REF}"
|
||||||
|
PARENT_SCOPE
|
||||||
|
)
|
||||||
|
set(${_hashvar}
|
||||||
|
"${HEAD_HASH}"
|
||||||
|
PARENT_SCOPE
|
||||||
|
)
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
function(git_describe _var)
|
||||||
|
if(NOT GIT_FOUND)
|
||||||
|
find_package(Git QUIET)
|
||||||
|
endif()
|
||||||
|
get_git_head_revision(refspec hash)
|
||||||
|
if(NOT GIT_FOUND)
|
||||||
|
set(${_var}
|
||||||
|
"GIT-NOTFOUND"
|
||||||
|
PARENT_SCOPE
|
||||||
|
)
|
||||||
|
return()
|
||||||
|
endif()
|
||||||
|
if(NOT hash)
|
||||||
|
set(${_var}
|
||||||
|
"HEAD-HASH-NOTFOUND"
|
||||||
|
PARENT_SCOPE
|
||||||
|
)
|
||||||
|
return()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# TODO sanitize if((${ARGN}" MATCHES "&&") OR (ARGN MATCHES "||") OR (ARGN MATCHES "\\;"))
|
||||||
|
# message("Please report the following error to the project!") message(FATAL_ERROR "Looks like
|
||||||
|
# someone's doing something nefarious with git_describe! Passed arguments ${ARGN}") endif()
|
||||||
|
|
||||||
|
# message(STATUS "Arguments to execute_process: ${ARGN}")
|
||||||
|
|
||||||
|
execute_process(
|
||||||
|
COMMAND "${GIT_EXECUTABLE}" describe ${hash} ${ARGN}
|
||||||
|
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
|
||||||
|
RESULT_VARIABLE res
|
||||||
|
OUTPUT_VARIABLE out
|
||||||
|
ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||||
|
)
|
||||||
|
if(NOT res EQUAL 0)
|
||||||
|
set(out "${out}-${res}-NOTFOUND")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(${_var}
|
||||||
|
"${out}"
|
||||||
|
PARENT_SCOPE
|
||||||
|
)
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
function(git_get_exact_tag _var)
|
||||||
|
git_describe(out --exact-match ${ARGN})
|
||||||
|
set(${_var}
|
||||||
|
"${out}"
|
||||||
|
PARENT_SCOPE
|
||||||
|
)
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
function(git_local_changes _var)
|
||||||
|
if(NOT GIT_FOUND)
|
||||||
|
find_package(Git QUIET)
|
||||||
|
endif()
|
||||||
|
get_git_head_revision(refspec hash)
|
||||||
|
if(NOT GIT_FOUND)
|
||||||
|
set(${_var}
|
||||||
|
"GIT-NOTFOUND"
|
||||||
|
PARENT_SCOPE
|
||||||
|
)
|
||||||
|
return()
|
||||||
|
endif()
|
||||||
|
if(NOT hash)
|
||||||
|
set(${_var}
|
||||||
|
"HEAD-HASH-NOTFOUND"
|
||||||
|
PARENT_SCOPE
|
||||||
|
)
|
||||||
|
return()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
execute_process(
|
||||||
|
COMMAND "${GIT_EXECUTABLE}" diff-index --quiet HEAD --
|
||||||
|
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
|
||||||
|
RESULT_VARIABLE res
|
||||||
|
OUTPUT_VARIABLE out
|
||||||
|
ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||||
|
)
|
||||||
|
if(res EQUAL 0)
|
||||||
|
set(${_var}
|
||||||
|
"CLEAN"
|
||||||
|
PARENT_SCOPE
|
||||||
|
)
|
||||||
|
else()
|
||||||
|
set(${_var}
|
||||||
|
"DIRTY"
|
||||||
|
PARENT_SCOPE
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
function(git_count_parent_commits _var)
|
||||||
|
if(NOT GIT_FOUND)
|
||||||
|
find_package(Git QUIET)
|
||||||
|
endif()
|
||||||
|
get_git_head_revision(refspec hash)
|
||||||
|
if(NOT GIT_FOUND)
|
||||||
|
set(${_var}
|
||||||
|
"GIT-NOTFOUND"
|
||||||
|
PARENT_SCOPE
|
||||||
|
)
|
||||||
|
return()
|
||||||
|
endif()
|
||||||
|
if(NOT hash)
|
||||||
|
set(${_var}
|
||||||
|
"HEAD-HASH-NOTFOUND"
|
||||||
|
PARENT_SCOPE
|
||||||
|
)
|
||||||
|
return()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
execute_process(
|
||||||
|
COMMAND "${GIT_EXECUTABLE}" rev-list --count HEAD
|
||||||
|
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
|
||||||
|
RESULT_VARIABLE res
|
||||||
|
OUTPUT_VARIABLE out
|
||||||
|
ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||||
|
)
|
||||||
|
if(res EQUAL 0)
|
||||||
|
set(${_var}
|
||||||
|
"${out}"
|
||||||
|
PARENT_SCOPE
|
||||||
|
)
|
||||||
|
else()
|
||||||
|
set(${_var}
|
||||||
|
"-1"
|
||||||
|
PARENT_SCOPE
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
endfunction()
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
#
|
||||||
|
# Internal file for GetGitRevisionDescription.cmake
|
||||||
|
#
|
||||||
|
# Requires CMake 2.6 or newer (uses the 'function' command)
|
||||||
|
#
|
||||||
|
# Original Author: 2009-2010 Ryan Pavlik <rpavlik@iastate.edu> <abiryan@ryand.net>
|
||||||
|
# http://academic.cleardefinition.com Iowa State University HCI Graduate Program/VRAC
|
||||||
|
#
|
||||||
|
# Copyright Iowa State University 2009-2010. Distributed under the Boost Software License, Version
|
||||||
|
# 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
|
||||||
|
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}" "@GIT_DATA@/head-ref" COPYONLY)
|
||||||
|
else()
|
||||||
|
configure_file("@GIT_DIR@/packed-refs" "@GIT_DATA@/packed-refs" COPYONLY)
|
||||||
|
file(READ "@GIT_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" "@GIT_DATA@/head-ref" COPYONLY)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(NOT HEAD_HASH)
|
||||||
|
file(READ "@GIT_DATA@/head-ref" HEAD_HASH LIMIT 1024)
|
||||||
|
string(STRIP "${HEAD_HASH}" HEAD_HASH)
|
||||||
|
endif()
|
||||||
|
|
@ -0,0 +1,62 @@
|
||||||
|
#
|
||||||
|
# This file is responsible for setting the following variables:
|
||||||
|
#
|
||||||
|
# ~~~
|
||||||
|
# BUILD_NUMBER (1035)
|
||||||
|
# PROJECT_VERSION (4.0.3)
|
||||||
|
# PROJECT_VERSION_FULL (4.0.3-BETA+1035.PR111.B4)
|
||||||
|
# PROJECT_VERSION_SUFFIX (-BETA+1035.PR111.B4)
|
||||||
|
# PROJECT_VERSION_SUFFIX_SHORT (+1035)
|
||||||
|
#
|
||||||
|
# The `PROJECT_VERSION` variable is set as soon as the file is included.
|
||||||
|
# To set the rest, the function `resolve_version_variables` has to be called.
|
||||||
|
#
|
||||||
|
# ~~~
|
||||||
|
|
||||||
|
# PROJECT_VERSION
|
||||||
|
file(READ "${CMAKE_SOURCE_DIR}/version.txt" content)
|
||||||
|
string(REGEX MATCH "([0-9]+)\.([0-9]+)\.([0-9]+)" result "${content}")
|
||||||
|
if(NOT result)
|
||||||
|
message(FATAL_ERROR "Failed to read version info from ${version_file}")
|
||||||
|
endif()
|
||||||
|
set(PROJECT_VERSION ${CMAKE_MATCH_0})
|
||||||
|
|
||||||
|
function(resolve_version_variables)
|
||||||
|
# BUILD_NUMBER
|
||||||
|
if(NOT BUILD_NUMBER)
|
||||||
|
git_count_parent_commits(BUILD_NUMBER)
|
||||||
|
set(ERRORS "GIT-NOTFOUND" "HEAD-HASH-NOTFOUND")
|
||||||
|
if(BUILD_NUMBER IN_LIST ERRORS)
|
||||||
|
message(WARNING "Failed to resolve build number: ${BUILD_NUMBER}. Setting to zero.")
|
||||||
|
set(BUILD_NUMBER "0")
|
||||||
|
endif()
|
||||||
|
set(BUILD_NUMBER
|
||||||
|
${BUILD_NUMBER}
|
||||||
|
PARENT_SCOPE
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# PROJECT_VERSION_SUFFIX
|
||||||
|
if(PROJECT_VERSION_SUFFIX STREQUAL "<auto>")
|
||||||
|
# TODO: set to +<sha>.dirty?.debug?
|
||||||
|
set(PROJECT_VERSION_SUFFIX "+${BUILD_NUMBER}.LOCAL")
|
||||||
|
set(PROJECT_VERSION_SUFFIX
|
||||||
|
"+${BUILD_NUMBER}.LOCAL"
|
||||||
|
PARENT_SCOPE
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# PROJECT_VERSION_SUFFIX_SHORT
|
||||||
|
if(PROJECT_VERSION_SUFFIX_SHORT STREQUAL "<auto>")
|
||||||
|
set(PROJECT_VERSION_SUFFIX_SHORT
|
||||||
|
"+${BUILD_NUMBER}"
|
||||||
|
PARENT_SCOPE
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# PROJECT_VERSION_FULL
|
||||||
|
set(PROJECT_VERSION_FULL
|
||||||
|
"${PROJECT_VERSION}${PROJECT_VERSION_SUFFIX}"
|
||||||
|
PARENT_SCOPE
|
||||||
|
)
|
||||||
|
endfunction()
|
||||||
|
|
@ -0,0 +1,122 @@
|
||||||
|
get_filename_component(PROJECT_CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" DIRECTORY)
|
||||||
|
get_filename_component(PROJECT_ROOT_DIR "${PROJECT_CMAKE_DIR}" DIRECTORY)
|
||||||
|
|
||||||
|
find_package(Python3 COMPONENTS Interpreter)
|
||||||
|
if(NOT Python3_FOUND)
|
||||||
|
message(FATAL_ERROR "Python3 not found.")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
function(get_recommended_gcc_version var)
|
||||||
|
execute_process(
|
||||||
|
COMMAND "${Python3_EXECUTABLE}" "${PROJECT_ROOT_DIR}/utils/bootstrap.py"
|
||||||
|
"--print-dependency-version" "gcc-arm-none-eabi"
|
||||||
|
OUTPUT_VARIABLE RECOMMENDED_VERSION
|
||||||
|
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||||
|
RESULT_VARIABLE RETVAL
|
||||||
|
)
|
||||||
|
|
||||||
|
if(NOT "${RETVAL}" STREQUAL "0")
|
||||||
|
message(FATAL_ERROR "Failed to obtain recommended gcc version from utils/bootstrap.py")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(${var}
|
||||||
|
${RECOMMENDED_VERSION}
|
||||||
|
PARENT_SCOPE
|
||||||
|
)
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
function(get_dependency_directory dependency var)
|
||||||
|
execute_process(
|
||||||
|
COMMAND "${Python3_EXECUTABLE}" "${PROJECT_ROOT_DIR}/utils/bootstrap.py"
|
||||||
|
"--print-dependency-directory" "${dependency}"
|
||||||
|
OUTPUT_VARIABLE DEPENDENCY_DIRECTORY
|
||||||
|
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||||
|
RESULT_VARIABLE RETVAL
|
||||||
|
)
|
||||||
|
|
||||||
|
if(NOT "${RETVAL}" STREQUAL "0")
|
||||||
|
message(FATAL_ERROR "Failed to find directory with ${dependency}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(${var}
|
||||||
|
${DEPENDENCY_DIRECTORY}
|
||||||
|
PARENT_SCOPE
|
||||||
|
)
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
function(objcopy target format suffix)
|
||||||
|
add_custom_command(
|
||||||
|
TARGET ${target} POST_BUILD
|
||||||
|
COMMAND "${CMAKE_OBJCOPY}" -O ${format} -S "$<TARGET_FILE:${target}>"
|
||||||
|
"${CMAKE_CURRENT_BINARY_DIR}/${target}${suffix}"
|
||||||
|
COMMENT "Generating ${format} from ${target}..."
|
||||||
|
)
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
function(report_size target)
|
||||||
|
add_custom_command(
|
||||||
|
TARGET ${target} POST_BUILD
|
||||||
|
COMMAND echo "" # visually separate the output
|
||||||
|
COMMAND "${CMAKE_SIZE_UTIL}" -B "$<TARGET_FILE:${target}>"
|
||||||
|
USES_TERMINAL
|
||||||
|
)
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
function(pack_firmware target fw_version build_number printer_type signing_key)
|
||||||
|
set(bin_firmware_path "${CMAKE_CURRENT_BINARY_DIR}/${target}.bin")
|
||||||
|
if(SIGNING_KEY)
|
||||||
|
set(sign_opts "--key" "${signing_key}")
|
||||||
|
else()
|
||||||
|
set(sign_opts "--no-sign")
|
||||||
|
endif()
|
||||||
|
add_custom_command(
|
||||||
|
TARGET ${target} POST_BUILD
|
||||||
|
COMMAND "${CMAKE_OBJCOPY}" -O binary -S "$<TARGET_FILE:${target}>" "${bin_firmware_path}"
|
||||||
|
COMMAND echo "" # visually separate the output
|
||||||
|
COMMAND
|
||||||
|
"${Python3_EXECUTABLE}" "${CMAKE_SOURCE_DIR}/utils/pack_fw.py" --version="${fw_version}"
|
||||||
|
--printer-type "${printer_type}" --printer-version "1" ${sign_opts} "${bin_firmware_path}"
|
||||||
|
--build-number "${build_number}"
|
||||||
|
)
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
function(create_dfu)
|
||||||
|
set(options)
|
||||||
|
set(one_value_args OUTPUT TARGET)
|
||||||
|
set(multi_value_args INPUT)
|
||||||
|
cmake_parse_arguments(CREATE_DFU "${options}" "${one_value_args}" "${multi_value_args}" ${ARGN})
|
||||||
|
|
||||||
|
add_custom_command(
|
||||||
|
TARGET "${CREATE_DFU_TARGET}" POST_BUILD
|
||||||
|
COMMAND "${Python3_EXECUTABLE}" "${CMAKE_SOURCE_DIR}/utils/dfu.py" create ${CREATE_DFU_INPUT}
|
||||||
|
"${CREATE_DFU_OUTPUT}"
|
||||||
|
)
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
function(add_link_dependency target file_path)
|
||||||
|
get_target_property(link_deps ${target} LINK_DEPENDS)
|
||||||
|
if(link_deps STREQUAL "link_deps-NOTFOUND")
|
||||||
|
set(link_deps "")
|
||||||
|
endif()
|
||||||
|
list(APPEND link_deps "${file_path}")
|
||||||
|
set_target_properties(${target} PROPERTIES LINK_DEPENDS "${link_deps}")
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
function(rfc1123_datetime var)
|
||||||
|
set(cmd
|
||||||
|
"from email.utils import formatdate; print(formatdate(timeval=None, localtime=False, usegmt=True))"
|
||||||
|
)
|
||||||
|
execute_process(
|
||||||
|
COMMAND "${Python3_EXECUTABLE}" -c "${cmd}"
|
||||||
|
OUTPUT_VARIABLE RFC1123_DATETIME
|
||||||
|
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||||
|
RESULT_VARIABLE RETVAL
|
||||||
|
)
|
||||||
|
if(NOT "${RETVAL}" STREQUAL "0")
|
||||||
|
message(FATAL_ERROR "Failed to obtain rfc1123 date time from Python")
|
||||||
|
endif()
|
||||||
|
set(${var}
|
||||||
|
${RFC1123_DATETIME}
|
||||||
|
PARENT_SCOPE
|
||||||
|
)
|
||||||
|
endfunction()
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
int main() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
#include "version.h"
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#define _STR(x) #x
|
||||||
|
#define STR(x) _STR(x)
|
||||||
|
|
||||||
|
const char project_version[] = STR(FW_VERSION);
|
||||||
|
|
||||||
|
const char project_version_full[] = STR(FW_VERSION_FULL);
|
||||||
|
|
||||||
|
const char project_version_suffix[] = STR(FW_VERSION_SUFFIX);
|
||||||
|
|
||||||
|
const char project_version_suffix_short[] = STR(FW_VERSION_SUFFIX_SHORT);
|
||||||
|
|
||||||
|
const int project_build_number = FW_BUILD_NUMBER;
|
||||||
|
|
||||||
|
#if (PRINTER_TYPE == PRINTER_PRUSA_MINI)
|
||||||
|
const char project_firmware_name[] = "Buddy_MINI";
|
||||||
|
#else
|
||||||
|
#error "unknown printer type"
|
||||||
|
#endif
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif //__cplusplus
|
||||||
|
|
||||||
|
/// Project's version (4.0.2)
|
||||||
|
extern const char project_version[];
|
||||||
|
|
||||||
|
/// Full project's version (4.0.3-BETA+1035.PR111.B4)
|
||||||
|
extern const char project_version_full[];
|
||||||
|
|
||||||
|
/// Project's version suffix (-BETA+1035.PR111.B4)
|
||||||
|
extern const char project_version_suffix[];
|
||||||
|
|
||||||
|
/// Project's short version suffix (+1035)
|
||||||
|
extern const char project_version_suffix_short[];
|
||||||
|
|
||||||
|
/// Project's build number (number of commits in a branch)
|
||||||
|
extern const int project_build_number;
|
||||||
|
|
||||||
|
/// Firmware name
|
||||||
|
extern const char project_firmware_name[];
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif //__cplusplus
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
#
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
#
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
#
|
||||||
|
|
@ -0,0 +1,197 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
#
|
||||||
|
# Bootstrap Script
|
||||||
|
#
|
||||||
|
# This script
|
||||||
|
# 1) records the recommended versions of dependencies, and
|
||||||
|
# 2) when run, checks that all of them are present and downloads
|
||||||
|
# them if they are not.
|
||||||
|
#
|
||||||
|
# pylint: disable=line-too-long
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import platform
|
||||||
|
import shutil
|
||||||
|
import stat
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import tarfile
|
||||||
|
import zipfile
|
||||||
|
from argparse import ArgumentParser
|
||||||
|
from pathlib import Path
|
||||||
|
from urllib.request import urlretrieve
|
||||||
|
|
||||||
|
project_root_dir = Path(__file__).resolve().parent.parent
|
||||||
|
dependencies_dir = project_root_dir / '.dependencies'
|
||||||
|
|
||||||
|
# All dependencies of this project.
|
||||||
|
#
|
||||||
|
# yapf: disable
|
||||||
|
dependencies = {
|
||||||
|
'ninja': {
|
||||||
|
'version': '1.9.0',
|
||||||
|
'url': {
|
||||||
|
'Linux': 'https://github.com/ninja-build/ninja/releases/download/v1.9.0/ninja-linux.zip',
|
||||||
|
'Windows': 'https://github.com/ninja-build/ninja/releases/download/v1.9.0/ninja-win.zip',
|
||||||
|
'Darwin': 'https://github.com/ninja-build/ninja/releases/download/v1.9.0/ninja-mac.zip',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'cmake': {
|
||||||
|
'version': '3.15.5',
|
||||||
|
'url': {
|
||||||
|
'Linux': 'https://github.com/Kitware/CMake/releases/download/v3.15.5/cmake-3.15.5-Linux-x86_64.tar.gz',
|
||||||
|
'Windows': 'https://github.com/Kitware/CMake/releases/download/v3.15.5/cmake-3.15.5-win64-x64.zip',
|
||||||
|
'Darwin': 'https://github.com/Kitware/CMake/releases/download/v3.15.5/cmake-3.15.5-Darwin-x86_64.tar.gz',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'avr8-gnu-toolchain': {
|
||||||
|
'version': '5.4.0',
|
||||||
|
'url': {
|
||||||
|
'Linux': 'https://xxxarmkeil.blob.core.windows.net/developer/Files/downloads/gnu-rm/7-2018q2/gcc-arm-none-eabi-7-2018-q2-update-linux.tar.bz2',
|
||||||
|
'Windows': 'https://xxxarmkeil.blob.core.windows.net/developer/Files/downloads/gnu-rm/7-2018q2/gcc-arm-none-eabi-7-2018-q2-update-win32.zip',
|
||||||
|
'Darwin': 'https://xxxarmkeil.blob.core.windows.net/developer/Files/downloads/gnu-rm/7-2018q2/gcc-arm-none-eabi-7-2018-q2-update-mac.tar.bz2',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'clang-format': {
|
||||||
|
'version': '9.0.0-noext',
|
||||||
|
'url': {
|
||||||
|
'Linux': 'https://prusa-buddy-firmware-dependencies.s3.eu-central-1.amazonaws.com/clang-format-9.0.0-linux.zip',
|
||||||
|
'Windows': 'https://prusa-buddy-firmware-dependencies.s3.eu-central-1.amazonaws.com/clang-format-9.0.0-noext-win.zip',
|
||||||
|
'Darwin': 'https://prusa-buddy-firmware-dependencies.s3.eu-central-1.amazonaws.com/clang-format-9.0.0-darwin.zip',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
pip_dependencies = ['ecdsa', 'polib']
|
||||||
|
# yapf: enable
|
||||||
|
|
||||||
|
|
||||||
|
def directory_for_dependency(dependency, version):
|
||||||
|
return dependencies_dir / (dependency + '-' + version)
|
||||||
|
|
||||||
|
|
||||||
|
def find_single_subdir(path: Path):
|
||||||
|
members = list(path.iterdir())
|
||||||
|
if path.is_dir() and len(members) > 1:
|
||||||
|
return path
|
||||||
|
elif path.is_dir() and len(members) == 1:
|
||||||
|
return find_single_subdir(members[0]) if members[0].is_dir() else path
|
||||||
|
else:
|
||||||
|
raise RuntimeError
|
||||||
|
|
||||||
|
|
||||||
|
def download_and_unzip(url: str, directory: Path):
|
||||||
|
"""Download a compressed file and extract it at `directory`."""
|
||||||
|
extract_dir = directory.with_suffix('.temp')
|
||||||
|
shutil.rmtree(directory, ignore_errors=True)
|
||||||
|
shutil.rmtree(extract_dir, ignore_errors=True)
|
||||||
|
|
||||||
|
print('Downloading ' + directory.name)
|
||||||
|
f, _ = urlretrieve(url, filename=None)
|
||||||
|
print('Extracting ' + directory.name)
|
||||||
|
if '.tar.bz2' in url or '.tar.gz' in url or '.tar.xz' in url:
|
||||||
|
obj = tarfile.open(f)
|
||||||
|
else:
|
||||||
|
obj = zipfile.ZipFile(f, 'r')
|
||||||
|
obj.extractall(path=str(extract_dir))
|
||||||
|
|
||||||
|
subdir = find_single_subdir(extract_dir)
|
||||||
|
shutil.move(str(subdir), str(directory))
|
||||||
|
shutil.rmtree(extract_dir, ignore_errors=True)
|
||||||
|
|
||||||
|
|
||||||
|
def run(*cmd):
|
||||||
|
process = subprocess.run([str(a) for a in cmd],
|
||||||
|
stdout=subprocess.PIPE,
|
||||||
|
check=True,
|
||||||
|
encoding='utf-8')
|
||||||
|
return process.stdout.strip()
|
||||||
|
|
||||||
|
|
||||||
|
def fix_executable_permissions(dependency, installation_directory):
|
||||||
|
to_fix = ('ninja', 'clang-format')
|
||||||
|
if dependency not in to_fix:
|
||||||
|
return
|
||||||
|
for fpath in installation_directory.iterdir():
|
||||||
|
if fpath.is_file and fpath.with_suffix('').name in to_fix:
|
||||||
|
st = os.stat(fpath)
|
||||||
|
os.chmod(fpath, st.st_mode | stat.S_IEXEC)
|
||||||
|
|
||||||
|
|
||||||
|
def recommended_version_is_available(dependency):
|
||||||
|
version = dependencies[dependency]['version']
|
||||||
|
directory = directory_for_dependency(dependency, version)
|
||||||
|
return directory.exists() and directory.is_dir()
|
||||||
|
|
||||||
|
|
||||||
|
def get_installed_pip_packages():
|
||||||
|
result = run(sys.executable, '-m', 'pip', 'list',
|
||||||
|
'--disable-pip-version-check', '--format', 'json')
|
||||||
|
data = json.loads(result)
|
||||||
|
return [(pkg['name'].lower(), pkg['version']) for pkg in data]
|
||||||
|
|
||||||
|
|
||||||
|
def install_dependency(dependency):
|
||||||
|
specs = dependencies[dependency]
|
||||||
|
installation_directory = directory_for_dependency(dependency,
|
||||||
|
specs['version'])
|
||||||
|
url = specs['url']
|
||||||
|
if isinstance(url, dict):
|
||||||
|
url = url[platform.system()]
|
||||||
|
download_and_unzip(url=url, directory=installation_directory)
|
||||||
|
fix_executable_permissions(dependency, installation_directory)
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> int:
|
||||||
|
parser = ArgumentParser()
|
||||||
|
# yapf: disable
|
||||||
|
parser.add_argument(
|
||||||
|
'--print-dependency-version', type=str,
|
||||||
|
help='Prints recommended version of given dependency and exits.')
|
||||||
|
parser.add_argument(
|
||||||
|
'--print-dependency-directory', type=str,
|
||||||
|
help='Prints installation directory of given dependency and exits.')
|
||||||
|
args = parser.parse_args(sys.argv[1:])
|
||||||
|
# yapf: enable
|
||||||
|
|
||||||
|
if args.print_dependency_version:
|
||||||
|
try:
|
||||||
|
version = dependencies[args.print_dependency_version]['version']
|
||||||
|
print(version)
|
||||||
|
return 0
|
||||||
|
except KeyError:
|
||||||
|
print('Unknown dependency "%s"' % args.print_dependency_version)
|
||||||
|
return 1
|
||||||
|
|
||||||
|
if args.print_dependency_directory:
|
||||||
|
try:
|
||||||
|
dependency = args.print_dependency_directory
|
||||||
|
version = dependencies[dependency]['version']
|
||||||
|
install_dir = directory_for_dependency(dependency, version)
|
||||||
|
print(install_dir)
|
||||||
|
return 0
|
||||||
|
except KeyError:
|
||||||
|
print('Unknown dependency "%s"' % args.print_dependency_directory)
|
||||||
|
return 1
|
||||||
|
|
||||||
|
# if no argument present, check and install dependencies
|
||||||
|
for dependency in dependencies:
|
||||||
|
if recommended_version_is_available(dependency):
|
||||||
|
continue
|
||||||
|
install_dependency(dependency)
|
||||||
|
|
||||||
|
# also, install pip packages
|
||||||
|
installed_pip_packages = get_installed_pip_packages()
|
||||||
|
for package in pip_dependencies:
|
||||||
|
is_installed = any(installed[0] == package
|
||||||
|
for installed in installed_pip_packages)
|
||||||
|
if is_installed:
|
||||||
|
continue
|
||||||
|
print('Installing Python package %s' % package)
|
||||||
|
run(sys.executable, '-m', 'pip', 'install', package,
|
||||||
|
'--disable-pip-version-check')
|
||||||
|
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
sys.exit(main())
|
||||||
|
|
@ -0,0 +1,441 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
import argparse
|
||||||
|
import os
|
||||||
|
import platform
|
||||||
|
import random
|
||||||
|
import re
|
||||||
|
import shutil
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import xml.etree.ElementTree as ET
|
||||||
|
from abc import ABC, abstractmethod, abstractproperty
|
||||||
|
from copy import deepcopy
|
||||||
|
from enum import Enum
|
||||||
|
from functools import lru_cache
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Dict, List, Optional
|
||||||
|
from uuid import uuid4
|
||||||
|
|
||||||
|
try:
|
||||||
|
from tqdm import tqdm
|
||||||
|
except ModuleNotFoundError:
|
||||||
|
|
||||||
|
def tqdm(iterable, *args, **kwargs):
|
||||||
|
return iterable
|
||||||
|
|
||||||
|
if os.isatty(sys.stdout.fileno()) and random.randint(0, 10) <= 1:
|
||||||
|
print('TIP: run `pip install -m tqdm` to get a nice progress bar')
|
||||||
|
|
||||||
|
project_root = Path(__file__).resolve().parent.parent
|
||||||
|
dependencies_dir = project_root / '.dependencies'
|
||||||
|
|
||||||
|
|
||||||
|
def bootstrap(*args, interactive=False, check=False):
|
||||||
|
"""Run the bootstrap script."""
|
||||||
|
bootstrap_py = project_root / 'utils' / 'bootstrap.py'
|
||||||
|
result = subprocess.run([sys.executable, str(bootstrap_py)] + list(args),
|
||||||
|
check=False,
|
||||||
|
encoding='utf-8',
|
||||||
|
stdout=None if interactive else subprocess.PIPE,
|
||||||
|
stderr=None if interactive else subprocess.PIPE)
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def project_version():
|
||||||
|
"""Return current project version (e. g. "4.0.3")"""
|
||||||
|
with open(project_root / 'version.txt', 'r') as f:
|
||||||
|
return f.read().strip()
|
||||||
|
|
||||||
|
|
||||||
|
@lru_cache()
|
||||||
|
def get_dependency(name):
|
||||||
|
install_dir = Path(
|
||||||
|
bootstrap('--print-dependency-directory', name,
|
||||||
|
check=True).stdout.strip())
|
||||||
|
suffix = '.exe' if platform.system() == 'Windows' else ''
|
||||||
|
if name == 'ninja':
|
||||||
|
return install_dir / ('ninja' + suffix)
|
||||||
|
elif name == 'cmake':
|
||||||
|
return install_dir / 'bin' / ('cmake' + suffix)
|
||||||
|
else:
|
||||||
|
return install_dir
|
||||||
|
|
||||||
|
|
||||||
|
class Printer(Enum):
|
||||||
|
"""Represents the -DPRINTER CMake option."""
|
||||||
|
|
||||||
|
MMU = 'MMU'
|
||||||
|
|
||||||
|
|
||||||
|
#class Bootloader(Enum):
|
||||||
|
# """Represents the -DBOOTLOADER CMake option."""
|
||||||
|
#
|
||||||
|
# NO = 'NO'
|
||||||
|
# EMPTY = 'EMPTY'
|
||||||
|
# YES = 'YES'
|
||||||
|
#
|
||||||
|
# @property
|
||||||
|
# def file_component(self):
|
||||||
|
# if self == Bootloader.NO:
|
||||||
|
# return 'NOBOOT'
|
||||||
|
# elif self == Bootloader.EMPTY:
|
||||||
|
# return 'EMPTYBOOT'
|
||||||
|
# elif self == Bootloader.YES:
|
||||||
|
# return 'BOOT'
|
||||||
|
# else:
|
||||||
|
# raise NotImplementedError
|
||||||
|
|
||||||
|
|
||||||
|
class BuildType(Enum):
|
||||||
|
"""Represents the -DCONFIG CMake option."""
|
||||||
|
|
||||||
|
DEBUG = 'DEBUG'
|
||||||
|
RELEASE = 'RELEASE'
|
||||||
|
|
||||||
|
|
||||||
|
#class HostTool(Enum):
|
||||||
|
# """Known host tools."""
|
||||||
|
#
|
||||||
|
# png2font = "png2font"
|
||||||
|
# bin2cc = "bin2cc"
|
||||||
|
# hex2dfu = "hex2dfu"
|
||||||
|
# makefsdata = "makefsdata"
|
||||||
|
|
||||||
|
|
||||||
|
class BuildConfiguration(ABC):
|
||||||
|
@abstractmethod
|
||||||
|
def get_cmake_cache_entries(self):
|
||||||
|
"""Convert the build configuration to CMake cache entries."""
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def get_cmake_flags(self, build_dir: Path) -> List[str]:
|
||||||
|
"""Return all CMake command-line flags required to build this configuration."""
|
||||||
|
|
||||||
|
@abstractproperty
|
||||||
|
def name(self):
|
||||||
|
"""Name of the configuration."""
|
||||||
|
|
||||||
|
def __hash__(self):
|
||||||
|
return hash(self.name)
|
||||||
|
|
||||||
|
|
||||||
|
class FirmwareBuildConfiguration(BuildConfiguration):
|
||||||
|
def __init__(self,
|
||||||
|
printer: Printer,
|
||||||
|
build_type: BuildType,
|
||||||
|
toolchain: Path = None,
|
||||||
|
generator: str = None,
|
||||||
|
version_suffix: str = None,
|
||||||
|
version_suffix_short: str = None,
|
||||||
|
custom_entries: List[str] = None):
|
||||||
|
self.printer = printer
|
||||||
|
self.build_type = build_type
|
||||||
|
self.toolchain = toolchain or FirmwareBuildConfiguration.default_toolchain(
|
||||||
|
)
|
||||||
|
self.generator = generator
|
||||||
|
self.version_suffix = version_suffix
|
||||||
|
self.version_suffix_short = version_suffix_short
|
||||||
|
self.custom_entries = custom_entries or []
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def default_toolchain() -> Path:
|
||||||
|
return Path(
|
||||||
|
__file__).resolve().parent.parent / 'cmake/GccArmNoneEabi.cmake'
|
||||||
|
|
||||||
|
def get_cmake_cache_entries(self):
|
||||||
|
entries = []
|
||||||
|
if self.generator.lower() == 'ninja':
|
||||||
|
entries.append(('CMAKE_MAKE_PROGRAM', 'FILEPATH',
|
||||||
|
str(get_dependency('ninja'))))
|
||||||
|
entries.extend([
|
||||||
|
('CMAKE_MAKE_PROGRAM', 'FILEPATH', str(get_dependency('ninja'))),
|
||||||
|
('PRINTER', 'STRING', self.printer.value),
|
||||||
|
('CMAKE_TOOLCHAIN_FILE', 'FILEPATH', str(self.toolchain)),
|
||||||
|
('CMAKE_BUILD_TYPE', 'STRING', self.build_type.value.title()),
|
||||||
|
('PROJECT_VERSION_SUFFIX', 'STRING', self.version_suffix or ''),
|
||||||
|
('PROJECT_VERSION_SUFFIX_SHORT', 'STRING',
|
||||||
|
self.version_suffix_short or ''),
|
||||||
|
])
|
||||||
|
entries.extend(self.custom_entries)
|
||||||
|
return entries
|
||||||
|
|
||||||
|
def get_cmake_flags(self, build_dir: Path) -> List[str]:
|
||||||
|
cache_entries = self.get_cmake_cache_entries()
|
||||||
|
flags = ['-D{}:{}={}'.format(*entry) for entry in cache_entries]
|
||||||
|
flags += ['-G', self.generator or 'Ninja']
|
||||||
|
flags += ['-S', str(Path(__file__).resolve().parent.parent)]
|
||||||
|
flags += ['-B', str(build_dir)]
|
||||||
|
return flags
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self):
|
||||||
|
components = [
|
||||||
|
self.printer.name,
|
||||||
|
self.build_type.value,
|
||||||
|
]
|
||||||
|
return '_'.join(components)
|
||||||
|
|
||||||
|
|
||||||
|
class BuildResult:
|
||||||
|
"""Represents a result of an attempt to build the project."""
|
||||||
|
|
||||||
|
def __init__(self, config_returncode: int, build_returncode: Optional[int],
|
||||||
|
stdout: Path, stderr: Path, products: List[Path]):
|
||||||
|
self.config_returncode = config_returncode
|
||||||
|
self.build_returncode = build_returncode
|
||||||
|
self.stdout = stdout
|
||||||
|
self.stderr = stderr
|
||||||
|
self.products = products
|
||||||
|
|
||||||
|
@property
|
||||||
|
def configuration_failed(self):
|
||||||
|
return self.config_returncode != 0
|
||||||
|
|
||||||
|
@property
|
||||||
|
def build_failed(self):
|
||||||
|
return self.build_returncode != 0 and self.build_returncode is not None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_failure(self):
|
||||||
|
return self.configuration_failed or self.build_failed
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return '<BuildResult config={self.config_returncode} build={self.build_returncode}>'.format(
|
||||||
|
self=self)
|
||||||
|
|
||||||
|
|
||||||
|
def build(configuration: BuildConfiguration,
|
||||||
|
build_dir: Path,
|
||||||
|
configure_only=False,
|
||||||
|
output_to_file=True) -> BuildResult:
|
||||||
|
"""Build a project with a single configuration."""
|
||||||
|
flags = configuration.get_cmake_flags(build_dir=build_dir)
|
||||||
|
|
||||||
|
# create the build directory
|
||||||
|
build_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
products = []
|
||||||
|
|
||||||
|
if output_to_file:
|
||||||
|
# stdout and stderr are saved to a file in the build directory
|
||||||
|
stdout_path = build_dir / 'stdout.txt'
|
||||||
|
stderr_path = build_dir / 'stderr.txt'
|
||||||
|
stdout = open(stdout_path, 'w')
|
||||||
|
stderr = open(stderr_path, 'w')
|
||||||
|
else:
|
||||||
|
stdout_path, stderr_path = None, None
|
||||||
|
stdout, stderr = None, None
|
||||||
|
|
||||||
|
# prepare the build
|
||||||
|
config_process = subprocess.run([str(get_dependency('cmake'))] + flags,
|
||||||
|
stdout=stdout,
|
||||||
|
stderr=stderr,
|
||||||
|
check=False)
|
||||||
|
if not configure_only and config_process.returncode == 0:
|
||||||
|
cmd = [
|
||||||
|
str(get_dependency('cmake')), '--build',
|
||||||
|
str(build_dir), '--config',
|
||||||
|
configuration.build_type.value.lower()
|
||||||
|
]
|
||||||
|
build_process = subprocess.run(cmd,
|
||||||
|
stdout=stdout,
|
||||||
|
stderr=stderr,
|
||||||
|
check=False)
|
||||||
|
build_returncode = build_process.returncode
|
||||||
|
products.extend(build_dir / fname for fname in [
|
||||||
|
'firmware', 'firmware.bin', 'firmware.bbf', 'firmware.dfu',
|
||||||
|
'firmware.map'
|
||||||
|
] if (build_dir / fname).exists())
|
||||||
|
else:
|
||||||
|
build_returncode = None
|
||||||
|
|
||||||
|
if stdout:
|
||||||
|
stdout.close()
|
||||||
|
if stderr:
|
||||||
|
stderr.close()
|
||||||
|
|
||||||
|
# collect the result and return
|
||||||
|
return BuildResult(config_returncode=config_process.returncode,
|
||||||
|
build_returncode=build_returncode,
|
||||||
|
stdout=stdout_path,
|
||||||
|
stderr=stderr_path,
|
||||||
|
products=products)
|
||||||
|
|
||||||
|
|
||||||
|
def store_products(products: List[Path], build_config: BuildConfiguration,
|
||||||
|
products_dir: Path):
|
||||||
|
"""Copy build products to a shared products directory."""
|
||||||
|
products_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
for product in products:
|
||||||
|
is_firmware = isinstance(build_config, FirmwareBuildConfiguration)
|
||||||
|
has_custom_suffix = is_firmware and (build_config.version_suffix !=
|
||||||
|
'<auto>')
|
||||||
|
if has_custom_suffix:
|
||||||
|
version = project_version()
|
||||||
|
name = build_config.name.lower(
|
||||||
|
) + '_' + version + build_config.version_suffix
|
||||||
|
else:
|
||||||
|
name = build_config.name.lower()
|
||||||
|
destination = products_dir / (name + product.suffix)
|
||||||
|
shutil.copy(product, destination)
|
||||||
|
|
||||||
|
|
||||||
|
def list_of(EnumType):
|
||||||
|
"""Create an argument-parser for comma-separated list of values of some Enum subclass."""
|
||||||
|
|
||||||
|
def convert(val):
|
||||||
|
if val == '':
|
||||||
|
return []
|
||||||
|
values = [p.lower() for p in val.split(',')]
|
||||||
|
if 'all' in values:
|
||||||
|
return list(EnumType)
|
||||||
|
else:
|
||||||
|
return [EnumType(v.upper()) for v in values]
|
||||||
|
|
||||||
|
convert.__name__ = EnumType.__name__
|
||||||
|
return convert
|
||||||
|
|
||||||
|
|
||||||
|
def cmake_cache_entry(arg):
|
||||||
|
match = re.fullmatch(r'(.*):(.*)=(.*)', arg)
|
||||||
|
if not match:
|
||||||
|
raise ValueError('invalid cmake entry; must be <NAME>:<TYPE>=<VALUE>')
|
||||||
|
return (match.group(1), match.group(2), match.group(3))
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
# yapf: disable
|
||||||
|
parser.add_argument(
|
||||||
|
'--printer',
|
||||||
|
type=list_of(Printer),
|
||||||
|
default=list(Printer),
|
||||||
|
help='Printer type (default: {default}).'.format(
|
||||||
|
default=','.join(str(p.value.lower()) for p in Printer)))
|
||||||
|
parser.add_argument(
|
||||||
|
'--build-type',
|
||||||
|
type=list_of(BuildType),
|
||||||
|
default='release',
|
||||||
|
help=('Build type (debug or release; default: release; '
|
||||||
|
'default for --generate-cproject: debug,release).'))
|
||||||
|
parser.add_argument(
|
||||||
|
'--version-suffix',
|
||||||
|
type=str,
|
||||||
|
default='<auto>',
|
||||||
|
help='Version suffix (e.g. -BETA+1035.PR111.B4)')
|
||||||
|
parser.add_argument(
|
||||||
|
'--version-suffix-short',
|
||||||
|
type=str,
|
||||||
|
default='<auto>',
|
||||||
|
help='Version suffix (e.g. +1035)')
|
||||||
|
parser.add_argument(
|
||||||
|
'--final',
|
||||||
|
action='store_true',
|
||||||
|
help='Set\'s --version-suffix and --version-suffix-short to empty string.')
|
||||||
|
parser.add_argument(
|
||||||
|
'--build-dir',
|
||||||
|
type=Path,
|
||||||
|
help='Specify a custom build directory to be used.')
|
||||||
|
parser.add_argument(
|
||||||
|
'--products-dir',
|
||||||
|
type=Path,
|
||||||
|
help='Directory to store built firmware (default: <build-dir>/products).')
|
||||||
|
parser.add_argument(
|
||||||
|
'-G', '--generator',
|
||||||
|
type=str,
|
||||||
|
default='Ninja',
|
||||||
|
help='Generator to be used by CMake (default=Ninja).')
|
||||||
|
parser.add_argument(
|
||||||
|
'--toolchain',
|
||||||
|
type=Path,
|
||||||
|
help='Path to a CMake toolchain file to be used.')
|
||||||
|
parser.add_argument(
|
||||||
|
'--host-tools',
|
||||||
|
action='store_true',
|
||||||
|
help=('Build host tools (png2font and others). '
|
||||||
|
'Turned on by default with --generate-cproject only.')
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--no-build',
|
||||||
|
action='store_true',
|
||||||
|
help='Do not build, configure the build only.'
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--no-store-output',
|
||||||
|
action='store_false',
|
||||||
|
help='Do not write build output to files - print it to console instead.'
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'-D', '--cmake-def',
|
||||||
|
action='append', type=cmake_cache_entry,
|
||||||
|
help='Custom CMake cache entries (e.g. -DCUSTOM_COMPILE_OPTIONS:STRING=-Werror)'
|
||||||
|
)
|
||||||
|
args = parser.parse_args(sys.argv[1:])
|
||||||
|
# yapf: enable
|
||||||
|
|
||||||
|
build_dir_root = args.build_dir or Path(
|
||||||
|
__file__).resolve().parent.parent / 'build'
|
||||||
|
products_dir_root = args.products_dir or (build_dir_root / 'products')
|
||||||
|
|
||||||
|
if args.final:
|
||||||
|
args.version_suffix = ''
|
||||||
|
args.version_suffix_short = ''
|
||||||
|
|
||||||
|
# Check all dependencis are installed
|
||||||
|
if bootstrap(interactive=True).returncode != 0:
|
||||||
|
print('bootstrap.py failed.')
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
# prepare configurations
|
||||||
|
configurations = [
|
||||||
|
FirmwareBuildConfiguration(
|
||||||
|
printer=printer,
|
||||||
|
build_type=build_type,
|
||||||
|
version_suffix=args.version_suffix,
|
||||||
|
version_suffix_short=args.version_suffix_short,
|
||||||
|
generator=args.generator,
|
||||||
|
custom_entries=args.cmake_def) for printer in args.printer
|
||||||
|
for build_type in args.build_type
|
||||||
|
]
|
||||||
|
|
||||||
|
# build everything
|
||||||
|
configurations_iter = tqdm(configurations)
|
||||||
|
results: Dict[BuildConfiguration, BuildResult] = dict()
|
||||||
|
for configuration in configurations_iter:
|
||||||
|
build_dir = build_dir_root / configuration.name.lower()
|
||||||
|
description = 'Building ' + configuration.name.lower()
|
||||||
|
if hasattr(configurations_iter, 'set_description'):
|
||||||
|
configurations_iter.set_description(description)
|
||||||
|
else:
|
||||||
|
print(description)
|
||||||
|
result = build(configuration,
|
||||||
|
build_dir=build_dir,
|
||||||
|
configure_only=args.no_build,
|
||||||
|
output_to_file=args.no_store_output is not False)
|
||||||
|
store_products(result.products, configuration, products_dir_root)
|
||||||
|
results[configuration] = result
|
||||||
|
|
||||||
|
# print results
|
||||||
|
print()
|
||||||
|
print('Building finished: {} success, {} failure(s).'.format(
|
||||||
|
sum(1 for result in results.values() if not result.is_failure),
|
||||||
|
sum(1 for result in results.values() if result.is_failure)))
|
||||||
|
failure = False
|
||||||
|
max_configname_len = max(len(config.name) for config in results)
|
||||||
|
for config, result in results.items():
|
||||||
|
if result.configuration_failed:
|
||||||
|
status = 'project configuration FAILED'
|
||||||
|
failure = True
|
||||||
|
elif result.build_failed:
|
||||||
|
status = 'build FAILED'
|
||||||
|
failure = True
|
||||||
|
else:
|
||||||
|
status = 'SUCCESS'
|
||||||
|
|
||||||
|
print(' {} {}'.format(
|
||||||
|
config.name.lower().ljust(max_configname_len, ' '), status))
|
||||||
|
if failure:
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
2.0.0
|
||||||
Loading…
Reference in New Issue