Merge pull request #3641 from vintagepc/build-with-cmake

Build the firmware with cmake
This commit is contained in:
vintagepc 2022-10-02 13:05:32 -04:00 committed by GitHub
commit d8c9c4450f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 1255 additions and 19 deletions

6
.gitignore vendored
View File

@ -2,8 +2,11 @@
/.settings
/.project
/.cproject
/.vscode
#cmake
/build/
/build_gen/
.dependencies
# Temporary configuration
/Firmware/Configuration_prusa.h
@ -24,3 +27,4 @@ __pycache__
# Generated files
/build-env/
/Firmware/Doc/
compile_commands.json

9
.vscode/cmake-kits.json vendored Normal file
View File

@ -0,0 +1,9 @@
[
{
"name": "Local_avr-gcc-none-eabi",
"toolchainFile": "${workspaceFolder}/cmake/LocalAvrGcc.cmake",
"cmakeSettings": {
"CMAKE_MAKE_PROGRAM": "${workspaceFolder}/.dependencies/ninja-1.10.2/ninja"
}
}
]

11
.vscode/cmake-variants.yaml vendored Normal file
View File

@ -0,0 +1,11 @@
buildType:
default: debug
choices:
debug:
short: Debug
long: Emit debug information
buildType: Debug
release:
short: Release
long: Optimize generated code
buildType: Release

9
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,9 @@
{
"cmake.configureOnOpen": true,
"cmake.copyCompileCommands": "${workspaceFolder}/compile_commands.json",
"cmake.cmakePath": "${workspaceFolder}/.dependencies/cmake-3.22.5/bin/cmake",
"files.insertFinalNewline": true,
"files.associations": {
"xlocale": "cpp"
}
}

View File

@ -1,23 +1,499 @@
cmake_minimum_required(VERSION 3.1)
cmake_minimum_required(VERSION 3.15)
include(cmake/Utilities.cmake)
include(cmake/GetGitRevisionDescription.cmake)
set (CMAKE_CXX_STANDARD 11)
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."
)
project(cmake_test)
# Prepare "Catch" library for other executables
set(CATCH_INCLUDE_DIR Catch2)
add_library(Catch INTERFACE)
target_include_directories(Catch INTERFACE ${CATCH_INCLUDE_DIR})
INCLUDE(cmake/ProjectVersion.cmake)
resolve_version_variables()
# Make test executable
set(TEST_SOURCES
Tests/tests.cpp
Tests/Example_test.cpp
Tests/Timer_test.cpp
Tests/AutoDeplete_test.cpp
Tests/PrusaStatistics_test.cpp
Firmware/Timer.cpp
SET(PROJECT_VERSION_FLAVOUR "" CACHE STRING "Firmware flavour to build - DEBUG, DEVEL, APLHA, BETA or RC")
SET(PROJECT_VERSION_FLAVOUR_REVISION "" CACHE STRING "Firmware flavour version, e.g. 1 for RC1, etc")
IF( NOT PROJECT_VERSION_FLAVOUR STREQUAL "")
SET(PROJECT_VERSION "${PROJECT_VERSION}-${PROJECT_VERSION_FLAVOUR}")
add_compile_definitions(FW_FLAVOR=${PROJECT_VERSION_FLAVOUR})
IF( NOT PROJECT_VERSION_FLAVOUR_REVISION STREQUAL "")
SET(PROJECT_VERSION "${PROJECT_VERSION}${PROJECT_VERSION_FLAVOUR_REVISION}")
add_compile_definitions(FW_FLAVERSION=${PROJECT_VERSION_FLAVOUR_REVISION})
ENDIF()
ENDIF()
# Inform user about the resolved settings
MESSAGE(STATUS "Project version: ${PROJECT_VERSION}")
MESSAGE(
STATUS "Project version with short suffix: ${PROJECT_VERSION}${PROJECT_VERSION_SUFFIX_SHORT}"
)
SET(FN_PREFIX "FW${PROJECT_VERSION}${PROJECT_VERSION_SUFFIX_SHORT}")
MESSAGE(WARNING "
***************** YOUR ATTENTION PLEASE *****************
CMake support is experimental. There is no guarantee at this time. If you have problems you are encouraged to fall back to the tried-and-true methods.
*********************** THANK YOU **********************
We now return to your regularly scheduled Firmware Build."
)
add_executable(tests ${TEST_SOURCES})
target_include_directories(tests PRIVATE Tests)
target_link_libraries(tests Catch)
OPTION(SECONDARY_LANGUAGES "Secondary language support in the firmware" ON)
SET(MAIN_LANGUAGES cs de es fr it pl CACHE STRING "The list of 'main' languages to be included, in the correct order")
SET(COMMUNITY_LANGUAGES nl ro hu hr sk sv no CACHE STRING "The list of community languages to be included, in the correct order")
SET(SELECTED_LANGUAGES ${MAIN_LANGUAGES} ${COMMUNITY_LANGUAGES})
get_dependency_directory(prusa3dboards PRUSA_BOARDS_DIR)
project(Prusa-Firmware)
FILE(STRINGS ${CMAKE_CURRENT_SOURCE_DIR}/Firmware/config.h MAX_SIZE_LINE REGEX "^#define \+LANG_SIZE_RESERVED \+")
STRING(REGEX MATCH "0x[0-9]+" MAX_SIZE_HEX "${MAX_SIZE_LINE}")
math(EXPR LANG_MAX_SIZE "${MAX_SIZE_HEX}" OUTPUT_FORMAT DECIMAL)
message("Language maximum size (from config.h): ${LANG_MAX_SIZE} bytes")
set (LANG_BIN_MAX 249856) # Ditto, this in xflash_layout.h but needs invocation of the preprocessor... :-/
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()
# append custom C/C++ flags
if(CUSTOM_COMPILE_OPTIONS)
string(REPLACE " " ";" CUSTOM_COMPILE_OPTIONS "${CUSTOM_COMPILE_OPTIONS}")
add_compile_options(${CUSTOM_COMPILE_OPTIONS})
endif()
#
# Global Compiler & Linker Configuration
#
# include symbols
add_compile_options(-g)
#
# Firmware - get file lists.
#
SET(FW_SOURCES
adc.cpp
backlight.cpp
BlinkM.cpp
bootapp.c
cardreader.cpp
cmdqueue.cpp
Configuration.cpp
ConfigurationStore.cpp
conv2str.cpp
Dcodes.cpp
eeprom.cpp
fancheck.cpp
Filament_sensor.cpp
first_lay_cal.cpp
heatbed_pwm.cpp
la10compat.cpp
language.c
lcd.cpp
Marlin_main.cpp
MarlinSerial.cpp
menu.cpp
mesh_bed_calibration.cpp
mesh_bed_leveling.cpp
messages.cpp
mmu2.cpp
mmu2_crc.cpp
mmu2_error_converter.cpp
mmu2_fsensor.cpp
mmu2_log.cpp
mmu2_power.cpp
mmu2_progress_converter.cpp
mmu2_protocol.cpp
mmu2_protocol_logic.cpp
mmu2_reporting.cpp
mmu2_serial.cpp
motion_control.cpp
optiboot_xflash.cpp
pat9125.cpp
planner.cpp
Prusa_farm.cpp
qr_solve.cpp
rbuf.c
Sd2Card.cpp
SdBaseFile.cpp
SdFatUtil.cpp
SdFile.cpp
SdVolume.cpp
Servo.cpp
sm4.c
sound.cpp
speed_lookuptable.cpp
spi.c
SpoolJoin.cpp
stepper.cpp
swi2c.c
swspi.cpp
Tcodes.cpp
temperature.cpp
timer02.c
Timer.cpp
tmc2130.cpp
tone04.c
twi.cpp
uart2.c
ultralcd.cpp
util.cpp
vector_3.cpp
xflash.c
xflash_dump.cpp
xyzcal.cpp
)
list(TRANSFORM FW_SOURCES PREPEND ${CMAKE_CURRENT_SOURCE_DIR}/Firmware/)
foreach(_FILE ${FW_SOURCES})
get_filename_component(_BASE ${_FILE} NAME)
set_property(SOURCE ${_FILE} APPEND_STRING PROPERTY COMPILE_FLAGS "-frandom-seed=${_BASE}.o")
endforeach()
set(AVR_SOURCES
wiring_digital.c
WInterrupts.c
wiring_pulse.c
hooks.c
wiring.c
wiring_analog.c
wiring_shift.c
CDC.cpp
PluggableUSB.cpp
HardwareSerial.cpp
HardwareSerial0.cpp
HardwareSerial1.cpp
HardwareSerial3.cpp
IPAddress.cpp
HardwareSerial2.cpp
Print.cpp
Stream.cpp
Tone.cpp
USBCore.cpp
WMath.cpp
WString.cpp
abi.cpp
main.cpp
# new.cpp # What happened to this? it was removed in 1.0.5-1 to 1.0.5.2?
)
list(TRANSFORM AVR_SOURCES PREPEND ${PRUSA_BOARDS_DIR}/cores/prusa_einsy_rambo/)
foreach(_FILE ${AVR_SOURCES})
get_filename_component(_BASE ${_FILE} NAME)
set_property(SOURCE ${_FILE} APPEND_STRING PROPERTY COMPILE_FLAGS "-frandom-seed=core/${_BASE}.o")
endforeach()
# optimizations
if(CMAKE_CROSSCOMPILING)
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
add_compile_options(-Og)
else()
add_compile_options(-Os)
endif()
# mcu related settings
set(MCU_FLAGS -mmcu=atmega2560 -DF_CPU=16000000L -DARDUINO=10819 -DARDUINO_AVR_PRUSA_EINSY_RAMBO -DARDUINO_ARCH_AVR)
add_compile_options(${MCU_FLAGS})
add_compile_options($<$<COMPILE_LANGUAGE:CXX>:-fno-exceptions>)
add_compile_options($<$<COMPILE_LANGUAGE:CXX>:-fno-threadsafe-statics>)
add_compile_options($<$<COMPILE_LANGUAGE:CXX>:-fno-rtti>)
add_compile_options(-Wall -Wextra -Wno-expansion-to-defined -ffunction-sections -fdata-sections -MMD -flto -fno-fat-lto-objects)
# split and gc sections
add_link_options(-Os -g -flto -Wl,--gc-sections -mmcu=atmega2560 -Wl,-u,vfprintf -lprintf_flt -lm )
# Create this target before we apply the GC options
add_library(avr_core STATIC ${AVR_SOURCES})
target_include_directories(avr_core PRIVATE
${PRUSA_BOARDS_DIR}/cores/prusa_einsy_rambo/
${PRUSA_BOARDS_DIR}/variants/prusa_einsy_rambo/
)
# disable exceptions and related metadata
add_compile_options(-fno-unwind-tables)
add_link_options(-Wl,--defsym,__exidx_start=0,--defsym,__exidx_end=0)
else()
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
add_compile_options(-O0)
else()
add_compile_options(-O2)
endif()
endif()
# enable all warnings (well, not all, but some)
add_compile_options(-Wsign-compare)
add_compile_options($<$<COMPILE_LANGUAGE:C>:-std=gnu11>)
# support _DEBUG macro (some code uses to recognize debug builds)
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
add_compile_definitions(_DEBUG)
endif()
# Setup language resources:
# file(GLOB LANG_VARIANTS RELATIVE ${PROJECT_SOURCE_DIR}/lang/po ${PROJECT_SOURCE_DIR}/lang/po/Firmware_??.po)
# string(REPLACE "Firmware_" "" LANG_VARIANTS "${LANG_VARIANTS}")
# string(REPLACE ".po" "" LANG_VARIANTS "${LANG_VARIANTS}")
# list(SORT LANG_VARIANTS)
# message("Languages found: ${LANG_VARIANTS}")
# Meta target to build absolutely everything
add_custom_target(ALL_FIRMWARE)
add_custom_target(ALL_ENGLISH)
add_custom_target(ALL_MULTILANG)
add_dependencies(ALL_FIRMWARE ALL_ENGLISH ALL_MULTILANG)
function(add_base_binary variant_name)
add_executable(${variant_name} ${FW_SOURCES} ${FW_HEADERS} ${VARIANT_CFG_FILE})
set_target_properties(${variant_name} PROPERTIES CXX_STANDARD 17)
target_include_directories(${variant_name} PRIVATE
${PRUSA_BOARDS_DIR}/cores/prusa_einsy_rambo/
${PRUSA_BOARDS_DIR}/variants/prusa_einsy_rambo/
${VARIANT_CFG_DIR} # Include the header for this variant.
${CMAKE_SOURCE_DIR}/Firmware
)
target_link_libraries(${variant_name} avr_core)
# # configure linker script
set(LINKER_SCRIPT ${PRUSA_BOARDS_DIR}/ldscripts/avr6.xn)
target_link_options(${variant_name} PUBLIC -Wl,-T,${LINKER_SCRIPT})
# limit the text section to 248K (256K - 8k reserved for the bootloader)
target_link_options(${variant_name} PUBLIC -Wl,--defsym=__TEXT_REGION_LENGTH__=248K)
# generate firmware.bin file
objcopy(${variant_name} "ihex" ".hex")
# produce ASM listing. Note we also specify the .map as a byproduct so it gets cleaned
# because link_options doesn't have a "generated outputs" feature.
add_custom_command(
TARGET ${variant_name} POST_BUILD COMMAND ${CMAKE_OBJDUMP} -CSd ${variant_name} > ${variant_name}.asm
BYPRODUCTS ${variant_name}.asm ${variant_name}.map
)
# inform about the firmware's size in terminal
add_custom_command(
TARGET ${variant_name} POST_BUILD COMMAND ${CMAKE_SIZE_UTIL} -C --mcu=atmega2560 ${variant_name}
)
report_size(${variant_name})
# generate linker map file
target_link_options(${variant_name} PUBLIC -Wl,-Map=${CMAKE_CURRENT_BINARY_DIR}/${variant_name}.map)
target_compile_definitions(${variant_name} PRIVATE
CMAKE_LANG_CONTROL
)
endfunction()
function(fw_add_variant variant_name)
# Create the Configuration_Prusa.h for this variant so it can be #included.
set(VARIANT_CFG_DIR "${CMAKE_CURRENT_BINARY_DIR}/include")
set(VARIANT_CFG_FILE "${VARIANT_CFG_DIR}/Configuration_prusa.h")
add_custom_command(OUTPUT ${VARIANT_CFG_FILE}
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_SOURCE_DIR}/Firmware/variants/${variant_name}.h ${VARIANT_CFG_FILE}
COMMENT "Generating Configuration_prusa.h for ${variant_name}"
BYPRODUCTS ${VARIANT_CFG_DIR}
)
STRING(REPLACE "1_75mm_" "" variant_name "${variant_name}")
STRING(REPLACE "-E3Dv6full" "" variant_name "${variant_name}")
SET(FW_EN "${variant_name}_EN-only")
SET(FW_MULTI "${variant_name}_Multilang")
add_base_binary(${FW_EN})
# target_compile_options(${variant_name} PRIVATE) # turn this on for lolz -Wdouble-promotion)
target_compile_definitions(${FW_EN} PUBLIC LANG_MODE=0)
add_custom_command(
TARGET ${FW_EN}
POST_BUILD
COMMAND ${CMAKE_OBJCOPY} -O ihex ${CMAKE_CURRENT_BINARY_DIR}/${FW_EN} ${CMAKE_BINARY_DIR}/${FN_PREFIX}-${FW_EN}.hex
BYPRODUCTS ${CMAKE_BINARY_DIR}/${FN_PREFIX}-${FW_EN}.hex
COMMENT "Generating ${variant_name} hex"
)
add_dependencies(ALL_ENGLISH "${FW_EN}")
if (NOT SECONDARY_LANGUAGES)
return() #Done, if no languages there's nothing else to do.
else()
add_base_binary(${FW_MULTI})
target_compile_definitions(${FW_MULTI} PUBLIC LANG_MODE=1)
endif()
#Construct language map
set(LANG_TMP_DIR ${CMAKE_BINARY_DIR}/${variant_name}/lang)
set(LANG_MAP ${LANG_TMP_DIR}/${variant_name}_lang.map)
set(LANG_FWBIN ${CMAKE_CURRENT_BINARY_DIR}/${variant_name}.bin)
set(LANG_FINAL_BIN ${LANG_TMP_DIR}/${variant_name}_lang.bin)
set(LANG_FINAL_HEX ${LANG_TMP_DIR}/${variant_name}_lang.hex)
add_custom_command(OUTPUT ${LANG_FWBIN}
COMMAND "${CMAKE_OBJCOPY}" -I ihex -O binary ${CMAKE_CURRENT_BINARY_DIR}/${variant_name}_Multilang.hex ${LANG_FWBIN}
DEPENDS ${FW_MULTI}
)
add_custom_command(OUTPUT ${LANG_MAP}
COMMAND ${CMAKE_SOURCE_DIR}/lang/lang-map.py "${FW_MULTI}" "${LANG_FWBIN}" > "${LANG_MAP}"
DEPENDS ${LANG_FWBIN}
)
set(LANG_BINS "")
foreach (LANG IN LISTS SELECTED_LANGUAGES)
set(LANG_BIN ${LANG_TMP_DIR}/${variant_name}_${LANG}.bin)
set(PO_FILE "${CMAKE_SOURCE_DIR}/lang/po/Firmware_${LANG}.po")
add_custom_command(OUTPUT ${LANG_BIN}
# COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/lang/lang-check.py --no-warning --map "${LANG_MAP}" "${PO_FILE}"
# COMMAND ${CMAKE_COMMAND} -E echo "Building lang_${LANG}.bin"
COMMAND ${CMAKE_SOURCE_DIR}/lang/lang-build.py ${LANG_MAP} ${PO_FILE} ${LANG_BIN}
# Check bin size:
COMMAND ${CMAKE_COMMAND} -DLANG_MAX_SIZE=${LANG_MAX_SIZE}
-DLANG_FILE=${LANG_BIN}
-P ${PROJECT_CMAKE_DIR}/Check_lang_size.cmake
DEPENDS ${LANG_MAP}
COMMENT "Generating ${variant_name}_${LANG}.bin from .po"
)
LIST(APPEND LANG_BINS ${LANG_BIN})
endforeach()
string(FIND ${variant_name} "MK3" HAS_XFLASH)
if (${HAS_XFLASH} GREATER_EQUAL 0)
add_custom_command( OUTPUT ${LANG_FINAL_BIN}
COMMAND ${CMAKE_COMMAND} -E cat ${LANG_BINS} > ${LANG_FINAL_BIN}
DEPENDS ${LANG_BINS}
COMMENT "Merging language binaries"
)
add_custom_command( OUTPUT ${LANG_FINAL_BIN}
COMMAND ${CMAKE_COMMAND} -DLANG_MAX_SIZE=${LANG_BIN_MAX}
-DLANG_FILE=${LANG_FINAL_BIN}
-P ${PROJECT_CMAKE_DIR}/Check_final_lang_bin_size.cmake
APPEND
)
add_custom_command( OUTPUT ${LANG_FINAL_HEX}
COMMAND ${CMAKE_OBJCOPY} -I binary -O ihex ${LANG_FINAL_BIN} ${LANG_FINAL_HEX}
DEPENDS ${LANG_FINAL_BIN}
COMMENT "Generating Hex for language data"
)
set(LANG_HEX ${CMAKE_BINARY_DIR}/${FN_PREFIX}-${variant_name}-Languages.hex)
add_custom_target(${variant_name}-language-hex
COMMAND ${CMAKE_COMMAND} -E copy ${FW_MULTI}.hex ${LANG_HEX}
COMMAND ${CMAKE_COMMAND} -E cat ${LANG_FINAL_HEX} >> ${LANG_HEX}
COMMENT "Generating final ${variant_name}-Languages.hex"
BYPRODUCTS ${LANG_HEX}
DEPENDS ${LANG_FINAL_HEX}
)
add_dependencies(ALL_MULTILANG ${variant_name}-language-hex)
else()
set (ALL_VARIANT_HEXES "")
# Non-xflash, e.g. MK2.5
foreach(LANG IN LISTS SELECTED_LANGUAGES)
SET(LANG_HEX_FN ${variant_name}-en_${LANG})
SET(LANG_HEX ${CMAKE_BINARY_DIR}/${FN_PREFIX}-${LANG_HEX_FN}.hex)
SET(LANG_BIN ${LANG_TMP_DIR}/${variant_name}_${LANG}.bin)
SET(LANG_FWBIN_TMP ${LANG_TMP_DIR}/${variant_name}-en_${LANG}.bin)
#Intermediate 2-lang bin
add_custom_command(OUTPUT ${LANG_FWBIN_TMP}
COMMAND ${CMAKE_COMMAND} -E copy ${LANG_FWBIN} ${LANG_FWBIN_TMP}
COMMAND ${CMAKE_SOURCE_DIR}/lang/lang-patchsec.py ${FW_MULTI} ${LANG_BIN} ${LANG_FWBIN_TMP}
DEPENDS ${LANG_FWBIN} ${LANG_BIN}
COMMENT "Generating ${variant_name}-en_${LANG}.bin"
)
#Final hex:
add_custom_target(${LANG_HEX_FN}
COMMAND ${CMAKE_OBJCOPY} -I binary -O ihex ${LANG_FWBIN_TMP} ${LANG_HEX}
BYPRODUCTS ${LANG_HEX}
DEPENDS ${LANG_FWBIN_TMP}
COMMENT "Creating ${LANG_HEX_FN}.hex"
)
LIST(APPEND ALL_VARIANT_HEXES ${LANG_HEX_FN})
endforeach()
add_custom_target("${variant_name}-All-Languages"
DEPENDS ${ALL_VARIANT_HEXES}
)
add_dependencies(ALL_MULTILANG "${variant_name}-All-Languages")
endif()
endfunction()
if(CMAKE_CROSSCOMPILING)
file(GLOB FW_VARIANTS RELATIVE ${PROJECT_SOURCE_DIR}/Firmware/variants ${PROJECT_SOURCE_DIR}/Firmware/variants/*.h)
foreach(THIS_VAR IN LISTS FW_VARIANTS)
string(REPLACE ".h" "" TRIMMED_NAME "${THIS_VAR}")
message("Variant added: ${TRIMMED_NAME}")
string(REPLACE "-E3Dv6full" "" DIR_NAME "${TRIMMED_NAME}")
string(REPLACE "1_75mm_" "" DIR_NAME "${DIR_NAME}")
# Generate a file in a subfolder so that we can organize things a little more neatly in VS code
FILE(MAKE_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/build_gen/${DIR_NAME})
FILE(WRITE ${CMAKE_CURRENT_SOURCE_DIR}/build_gen/${DIR_NAME}/CMakeLists.txt "project(${DIR_NAME})\nfw_add_variant(${TRIMMED_NAME})")
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/build_gen/${DIR_NAME})
#fw_add_variant(${TRIMMED_NAME})
endforeach(THIS_VAR IN LISTS FW_VARIANTS)
endif()
if(NOT CMAKE_CROSSCOMPILING)
# do not build the firmware by default (tests are the focus if not crosscompiling)
project(cmake_test)
# Prepare "Catch" library for other executables
set(CATCH_INCLUDE_DIR Catch2)
add_library(Catch INTERFACE)
target_include_directories(Catch INTERFACE ${CATCH_INCLUDE_DIR})
# Make test executable
set(TEST_SOURCES
Tests/tests.cpp
Tests/Example_test.cpp
Tests/Timer_test.cpp
Tests/AutoDeplete_test.cpp
Tests/PrusaStatistics_test.cpp
Firmware/Timer.cpp
Firmware/AutoDeplete.cpp
)
add_executable(tests ${TEST_SOURCES})
target_include_directories(tests PRIVATE Tests)
target_link_libraries(tests Catch)
endif()

View File

@ -60,9 +60,13 @@
#define TMC2130_SPCR SPI_SPCR(TMC2130_SPI_RATE, 1, 1, 1, 0)
#define TMC2130_SPSR SPI_SPSR(TMC2130_SPI_RATE)
// This is set by the cmake build to be able to take control of
// the language flag, without breaking existing build mechanisms.
#ifndef CMAKE_LANG_CONTROL
//LANG - Multi-language support
//#define LANG_MODE 0 // primary language only
#define LANG_MODE 1 // sec. language support
#endif
#define LANG_SIZE_RESERVED 0x3500 // reserved space for secondary language (13568 bytes).
// 0x3D00 Maximum 15616 bytes as it depends on xflash_layout.h

View File

@ -0,0 +1,11 @@
cmake_minimum_required(VERSION 3.18)
FILE(SIZE ${LANG_FILE} FILE_SIZE)
get_filename_component(FILE_BASE ${LANG_FILE} NAME)
MATH(EXPR PADDED_SIZE "((${FILE_SIZE}+4096-1) / 4096 * 4096 )")
message(STATUS "${FILE_BASE} raw size ${FILE_SIZE} bytes (${PADDED_SIZE} b padded)")
if(${PADDED_SIZE} GREATER ${LANG_MAX_SIZE})
message(FATAL_ERROR "Language file ${FILE_BASE} (${PADDED_SIZE}b) exceeds maximum allowed size of ${LANG_MAX_SIZE} bytes - Aborting!")
else()
MATH(EXPR SIZE_PCT "( ${PADDED_SIZE} * 100) / ${LANG_MAX_SIZE} " )
message(STATUS "Language file ${FILE_BASE} is ${PADDED_SIZE} bytes, ${SIZE_PCT}% of allowed space - OK")
endif()

View File

@ -0,0 +1,9 @@
cmake_minimum_required(VERSION 3.18)
FILE(SIZE ${LANG_FILE} FILE_SIZE)
get_filename_component(FILE_BASE ${LANG_FILE} NAME)
if(${FILE_SIZE} GREATER ${LANG_MAX_SIZE})
message(FATAL_ERROR "Language file ${FILE_BASE} (${FILE_SIZE}b) exceeds maximum allowed size of ${LANG_MAX_SIZE} bytes - Aborting!")
else()
MATH(EXPR SIZE_PCT "( ${FILE_SIZE} * 100) / ${LANG_MAX_SIZE} " )
message(STATUS "Language file ${FILE_BASE} is ${FILE_SIZE} bytes, ${SIZE_PCT}% of allowed space - OK")
endif()

View File

@ -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()

View File

@ -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()

102
cmake/LocalAvrGcc.cmake Normal file
View File

@ -0,0 +1,102 @@
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 avr)
set(CMAKE_CROSSCOMPILING 1)
get_dependency_directory("avr-gcc" AVR_TOOLCHAIN_DIR)
message( "tc dir is ${AVR_TOOLCHAIN_DIR}")
#
# 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 avr-)
#
# Looking up the toolchain
#
if(AVR_TOOLCHAIN_DIR)
# using toolchain set by AvrGcc.cmake (locked version)
set(BINUTILS_PATH "${AVR_TOOLCHAIN_DIR}/bin")
else()
# search for ANY avr-gcc toolchain
execute_process(
COMMAND ${UTIL_SEARCH_CMD} ${TOOLCHAIN_PREFIX}gcc
OUTPUT_VARIABLE AVR_GCC_PATH
OUTPUT_STRIP_TRAILING_WHITESPACE
RESULT_VARIABLE FIND_RESULT
)
# found?
if(NOT "${FIND_RESULT}" STREQUAL "0")
message(FATAL_ERROR "avr-gcc not found")
endif()
get_filename_component(BINUTILS_PATH "${AVR_GCC_PATH}" DIRECTORY)
get_filename_component(AVR_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(CMAKE_C_COMPILER
"${BINUTILS_PATH}/${TOOLCHAIN_PREFIX}gcc${EXECUTABLE_SUFFIX}"
CACHE FILEPATH "" FORCE
)
set(CMAKE_ASM_COMPILER
"${BINUTILS_PATH}/${TOOLCHAIN_PREFIX}gcc${EXECUTABLE_SUFFIX}"
CACHE FILEPATH "" FORCE
)
set(CMAKE_CXX_COMPILER
"${BINUTILS_PATH}/${TOOLCHAIN_PREFIX}g++${EXECUTABLE_SUFFIX}"
CACHE FILEPATH "" FORCE
)
set(CMAKE_EXE_LINKER_FLAGS_INIT
""
CACHE STRING "" FORCE
)
set(CMAKE_ASM_COMPILE_OBJECT
"<CMAKE_ASM_COMPILER> <DEFINES> <FLAGS> -o <OBJECT> -c <SOURCE>"
CACHE STRING "" FORCE
)
set(CMAKE_AR
"${BINUTILS_PATH}/${TOOLCHAIN_PREFIX}gcc-ar${EXECUTABLE_SUFFIX}"
CACHE FILEPATH "ar" FORCE
)
set(CMAKE_RANLIB
"${BINUTILS_PATH}/${TOOLCHAIN_PREFIX}gcc-ranlib${EXECUTABLE_SUFFIX}"
CACHE FILEPATH "ranlib" FORCE
)
set(CMAKE_OBJCOPY
"${BINUTILS_PATH}/${TOOLCHAIN_PREFIX}objcopy${EXECUTABLE_SUFFIX}"
CACHE INTERNAL "objcopy tool"
)
set(CMAKE_OBJDUMP
"${BINUTILS_PATH}/${TOOLCHAIN_PREFIX}objdump${EXECUTABLE_SUFFIX}"
CACHE INTERNAL "objdump tool"
)
set(CMAKE_SIZE_UTIL
"${BINUTILS_PATH}/${TOOLCHAIN_PREFIX}size${EXECUTABLE_SUFFIX}"
CACHE INTERNAL "size tool"
)
set(CMAKE_FIND_ROOT_PATH "${AVR_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)

View File

@ -0,0 +1,71 @@
#
# 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.
#
# ~~~
FILE(STRINGS ${CMAKE_CURRENT_SOURCE_DIR}/Firmware/Configuration.h CFG_VER_DATA REGEX "#define FW_[A-Z]+ ([0-9]+)" )
LIST(GET CFG_VER_DATA 0 PROJECT_VERSION_MAJOR)
LIST(GET CFG_VER_DATA 1 PROJECT_VERSION_MINOR)
LIST(GET CFG_VER_DATA 2 PROJECT_VERSION_REV)
STRING(REGEX MATCH "FW_MAJOR ([0-9]+)" PROJECT_VERSION_MAJOR "${PROJECT_VERSION_MAJOR}")
SET(PROJECT_VERSION_MAJOR "${CMAKE_MATCH_1}")
STRING(REGEX MATCH "FW_MINOR ([0-9]+)" PROJECT_VERSION_MINOR "${PROJECT_VERSION_MINOR}")
SET(PROJECT_VERSION_MINOR ${CMAKE_MATCH_1})
STRING(REGEX MATCH "FW_REVISION +([0-9]+)" PROJECT_VERSION_REV "${PROJECT_VERSION_REV}")
SET(PROJECT_VERSION_REV ${CMAKE_MATCH_1})
SET(PROJECT_VERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_REV}")
SET(PROJECT_VERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_REV}" PARENT_SCOPE)
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()

64
cmake/Utilities.cmake Normal file
View File

@ -0,0 +1,64 @@
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" "avr-gcc"
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}..."
BYPRODUCTS "${CMAKE_CURRENT_BINARY_DIR}/${target}${suffix}"
)
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()

197
utils/bootstrap.py Executable file
View File

@ -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.10.2',
'url': {
'Linux': 'https://github.com/ninja-build/ninja/releases/download/v1.10.2/ninja-linux.zip',
'Windows': 'https://github.com/ninja-build/ninja/releases/download/v1.10.2/ninja-win.zip',
'Darwin': 'https://github.com/ninja-build/ninja/releases/download/v1.10.2/ninja-mac.zip',
},
},
'cmake': {
'version': '3.22.5',
'url': {
'Linux': 'https://github.com/Kitware/CMake/releases/download/v3.22.5/cmake-3.22.5-linux-x86_64.tar.gz',
'Windows': 'https://github.com/Kitware/CMake/releases/download/v3.22.5/cmake-3.22.5-windows-x86_64.zip',
'Darwin': 'https://github.com/Kitware/CMake/releases/download/v3.22.5/cmake-3.22.5-macos-universal.tar.gz',
},
},
'avr-gcc': {
'version': '7.3.0',
'url': {
'Linux': 'http://downloads.arduino.cc/tools/avr-gcc-7.3.0-atmel3.6.1-arduino7-x86_64-pc-linux-gnu.tar.bz2',
'Windows': 'http://downloads.arduino.cc/tools/avr-gcc-7.3.0-atmel3.6.1-arduino7-i686-w64-mingw32.zip',
'Darwin': 'http://downloads.arduino.cc/tools/avr-gcc-7.3.0-atmel3.6.1-arduino7-x86_64-apple-darwin14.tar.bz2',
},
},
'prusa3dboards': {
'version': '1.0.5-2',
'url': {
'Linux': 'https://raw.githubusercontent.com/prusa3d/Arduino_Boards/devel/IDE_Board_Manager/prusa3dboards-1.0.5-2.tar.bz2',
'Windows': 'https://raw.githubusercontent.com/prusa3d/Arduino_Boards/devel/IDE_Board_Manager/prusa3dboards-1.0.5-2.tar.bz2',
'Darwin': 'https://raw.githubusercontent.com/prusa3d/Arduino_Boards/devel/IDE_Board_Manager/prusa3dboards-1.0.5-2.tar.bz2',
}
},
}
pip_dependencies = ["pyelftools","polib","regex"]
# 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())