WIP/proof-of-concept

This commit is contained in:
VintagePC 2022-07-26 19:33:09 -04:00
parent 8e72470afc
commit f03e3c40ee
9 changed files with 629 additions and 19 deletions

4
.gitignore vendored
View File

@ -2,7 +2,8 @@
/.settings
/.project
/.cproject
/.vscode
/build/
# Temporary configuration
/Firmware/Configuration_prusa.h
@ -23,3 +24,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_gcc-avr-none-eabi",
"toolchainFile": "${workspaceFolder}/cmake/LocalAvrGcc.cmake",
"cmakeSettings": {
"CMAKE_MAKE_PROGRAM": "${workspaceFolder}/.dependencies/ninja-1.9.0/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,7 +1,212 @@
cmake_minimum_required(VERSION 3.1)
cmake_minimum_required(VERSION 3.15)
include(cmake/Utilities.cmake)
set (CMAKE_CXX_STANDARD 11)
project(Prusa-Firmware)
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)
# 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)
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_compile_options($<$<COMPILE_LANGUAGE:CXX>:-fno-rtti>)
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(-Wall -Wsign-compare)
add_compile_options($<$<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()
#
# Firmware - get file lists.
#
file(GLOB FW_SOURCES RELATIVE ${PROJECT_SOURCE_DIR} ${PROJECT_SOURCE_DIR}/Firmware/*.c*)
file(GLOB FW_HEADERS RELATIVE ${PROJECT_SOURCE_DIR} ${PROJECT_SOURCE_DIR}/Firmware/*.h*)
file(GLOB AVR_SOURCES RELATIVE ${PROJECT_SOURCE_DIR} ${PROJECT_SOURCE_DIR}/.dependencies/1.8.19-1.0.5-1-linux-64/portable/packages/PrusaResearch/hardware/avr/1.0.5-1/cores/prusa_einsy_rambo/*.c*)
# 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}")
message("Languages found: ${LANG_VARIANTS}")
add_library(avr_core STATIC ${AVR_SOURCES})
target_include_directories(avr_core PRIVATE
${PROJECT_SOURCE_DIR}/.dependencies/1.8.19-1.0.5-1-linux-64/portable/packages/PrusaResearch/hardware/avr/1.0.5-1/cores/prusa_einsy_rambo/
${PROJECT_SOURCE_DIR}/.dependencies/1.8.19-1.0.5-1-linux-64/portable/packages/PrusaResearch/hardware/avr/1.0.5-1/variants/prusa_einsy_rambo/
)
target_compile_options(avr_core PUBLIC -mmcu=atmega2560)
function(fw_add_variant variant_name)
add_executable(${variant_name} ${FW_SOURCES} ${FW_HEADERS})
set_target_properties(${variant_name} PROPERTIES CXX_STANDARD 14)
# # configure linker script
set(LINKER_SCRIPT ${PROJECT_SOURCE_DIR}/.dependencies/1.8.19-1.0.5-1-linux-64/portable/packages/PrusaResearch/hardware/avr/1.0.5-1/ldscripts/avr6.xn)
target_link_options(${variant_name} PUBLIC -Wl,-T,${LINKER_SCRIPT})
add_link_dependency(${variant_name} ${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=${variant_name}.map)
target_include_directories(${variant_name} PRIVATE Firmware
${PROJECT_SOURCE_DIR}/.dependencies/1.8.19-1.0.5-1-linux-64/portable/packages/PrusaResearch/hardware/avr/1.0.5-1/cores/prusa_einsy_rambo/
${PROJECT_SOURCE_DIR}/.dependencies/1.8.19-1.0.5-1-linux-64/portable/packages/PrusaResearch/hardware/avr/1.0.5-1/variants/prusa_einsy_rambo/
${PROJECT_SOURCE_DIR}/cmake/helpers/ # Add our magic config helper :)
)
target_compile_options(${variant_name} PRIVATE) # turn this on for lolz -Wdouble-promotion)
string(REPLACE "-" "_" DEFINE_NAME "${variant_name}")
target_compile_definitions(${variant_name} PRIVATE H${DEFINE_NAME} ARDUINO=10600 __AVR_ATmega2560__)
target_link_libraries(${variant_name} avr_core)
#Construct language map
set(LANG_MAP ${CMAKE_CURRENT_BINARY_DIR}/lang/${variant_name}_lang.map)
set(LANG_FWBIN ${CMAKE_BINARY_DIR}/${variant_name}.bin)
set(LANG_FINAL_BIN ${CMAKE_CURRENT_BINARY_DIR}/lang/${variant_name}_lang.bin)
set(LANG_FINAL_HEX ${CMAKE_CURRENT_BINARY_DIR}/lang/${variant_name}_lang.hex)
add_custom_command(OUTPUT ${LANG_FWBIN}
COMMAND "${CMAKE_OBJCOPY}" -I ihex -O binary ${CMAKE_BINARY_DIR}/${variant_name}.hex ${LANG_FWBIN}
DEPENDS ${variant_name}
)
add_custom_command(OUTPUT ${LANG_MAP}
COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/lang/lang-map.py "${CMAKE_BINARY_DIR}/${variant_name}" "${LANG_FWBIN}" > "${LANG_MAP}"
DEPENDS ${LANG_FWBIN}
)
set(LANG_BINS "")
foreach (LANG IN LISTS LANG_VARIANTS)
set(LANG_BIN ${CMAKE_CURRENT_BINARY_DIR}/lang/${variant_name}_${LANG}.bin)
set(PO_FILE "${CMAKE_CURRENT_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_CURRENT_SOURCE_DIR}/lang/lang-build.py ${LANG_MAP} ${PO_FILE} ${LANG_BIN}
DEPENDS ${LANG_MAP}
COMMENT "Generating ${variant_name}_${LANG}.bin from .po"
)
LIST(APPEND LANG_BINS ${LANG_BIN})
endforeach()
add_custom_command( OUTPUT ${LANG_FINAL_BIN}
# TODO - needs differentiation for platforms, e.g. copy /b on Win
COMMAND cat ${LANG_BINS} > ${LANG_FINAL_BIN}
DEPENDS ${LANG_BINS}
COMMENT "Merging language binaries"
)
add_custom_command( OUTPUT ${LANG_FINAL_HEX}
# TODO - needs differentiation for platforms, e.g. copy /b on Win
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}/${variant_name}-lang.hex)
add_custom_target(${variant_name}-languages
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/${variant_name}.hex ${LANG_HEX}
COMMAND cat ${LANG_FINAL_HEX} >> ${LANG_HEX}
COMMENT "Generating final ${variant_name}-lang.hex"
BYPRODUCTS ${LANG_HEX}
DEPENDS ${LANG_FINAL_HEX}
)
endfunction()
if(CMAKE_CROSSCOMPILING)
add_custom_target(All_Firmware)
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}")
fw_add_variant(${TRIMMED_NAME})
add_dependencies(All_Firmware ${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
@ -22,3 +227,5 @@ set(TEST_SOURCES
add_executable(tests ${TEST_SOURCES})
target_include_directories(tests PRIVATE Tests)
target_link_libraries(tests Catch)
endif()

93
cmake/LocalAvrGcc.cmake Normal file
View File

@ -0,0 +1,93 @@
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)
set(AVR_TOOLCHAIN_DIR "${PROJECT_CMAKE_DIR}/../.dependencies/1.8.19-1.0.5-1-linux-64/hardware/tools/avr/")
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_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)

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" "gcc-avr"
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()

View File

@ -0,0 +1,24 @@
#ifdef H1_75mm_MK25_RAMBo10a_E3Dv6full
#include "variants/1_75mm_MK25-RAMBo10a-E3Dv6full.h"
#endif
#ifdef H1_75mm_MK25_RAMBo13a_E3Dv6full
#include "variants/1_75mm_MK25-RAMBo13a-E3Dv6full.h"
#endif
#ifdef H1_75mm_MK25S_RAMBo10a_E3Dv6full
#include "variants/1_75mm_MK25S-RAMBo10a-E3Dv6full.h"
#endif
#ifdef H1_75mm_MK25S_RAMBo13a_E3Dv6full
#include "variants/1_75mm_MK25S-RAMBo13a-E3Dv6full.h"
#endif
#ifdef H1_75mm_MK2_RAMBo10a_E3Dv6full
#include "variants/1_75mm_MK2-RAMBo10a-E3Dv6full.h"
#endif
#ifdef H1_75mm_MK2_RAMBo13a_E3Dv6full
#include "variants/1_75mm_MK2-RAMBo13a-E3Dv6full.h"
#endif
#ifdef H1_75mm_MK3_EINSy10a_E3Dv6full
#include "variants/1_75mm_MK3-EINSy10a-E3Dv6full.h"
#endif
#ifdef H1_75mm_MK3S_EINSy10a_E3Dv6full
#include "variants/1_75mm_MK3S-EINSy10a-E3Dv6full.h"
#endif

191
utils/bootstrap.py Executable file
View File

@ -0,0 +1,191 @@
#!/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',
},
},
'gcc-avr': {
# dummy placeholder (currently downloading cmake just for the sake of a valid url/zip archive)
# ... we truly need the binaries! :)
'version': '0.0.0',
'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',
}
},
}
pip_dependencies = []
# 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())