#!/bin/bash set -e self=$0 defconfig_command="$self defconfig" clean_command="$self clean" cmake_build_dir="build" cmake_cache_file="build/CMakeCache.txt" cmake_user_build_dir="user/build" cmake_kernel_build_dir="kernel/build" cmake_libc_build_dirs="user/musl-1.1.24/build user/musl-1.1.24/obj" cmake_script_dir="scripts/build/cmake" cmake_init_cache_default="$cmake_script_dir/LoadConfigDefault.cmake" cmake_init_cache_ask="$cmake_script_dir/LoadConfigAsk.cmake" cmake_init_cache_abort="$cmake_script_dir/LoadConfigAbort.cmake" cmake_init_cache_dump="$cmake_script_dir/DumpConfig.cmake" config_file=".config" defconfig_dir="scripts/build/defconfigs" RED='\033[0;31m' BLUE='\033[0;34m' GREEN='\033[0;32m' ORANGE='\033[0;33m' BOLD='\033[1m' NONE='\033[0m' _echo_info() { echo -e "${BOLD}$@${NONE}" } _echo_succ() { echo -e "${GREEN}${BOLD}$@${NONE}" } _echo_warn() { echo -e "${ORANGE}${BOLD}$@${NONE}" } _echo_err() { echo -e "${RED}${BOLD}$@${NONE}" } defconfig() { if [ -d $cmake_build_dir ]; then _echo_err "There exists a build directory, please run \`$clean_command\` first" exit 1 fi if [ -z "$1" ]; then plat="raspi3" else plat="$1" fi _echo_info "Generating default config file for \`$plat\` platform..." cp $defconfig_dir/${plat}.config $config_file _config_default _sync_config_with_cache _echo_succ "Default config written to \`$config_file\` file." } _check_config_file() { if [ ! -f $config_file ]; then _echo_err "There is no \`.config\` file, please run \`$defconfig_command\` first" exit 1 fi } _config_default() { _echo_info "Configuring CMake..." cmake -B $cmake_build_dir -C $cmake_init_cache_default } _config_ask() { _echo_info "Configuring CMake..." cmake -B $cmake_build_dir -C $cmake_init_cache_ask } _sync_config_with_cache() { cmake -N -B $cmake_build_dir -C $cmake_init_cache_dump >/dev/null } config() { _check_config_file _config_ask _sync_config_with_cache _echo_succ "Config syned to \`$config_file\` file." } menuconfig() { _check_config_file _config_default echo _echo_warn "Note: In the menu config view, press C to save, Q to quit." read -p "Now press Enter to continue..." ccmake -B $cmake_build_dir _sync_config_with_cache _echo_succ "Config saved to \`$config_file\` file." } build() { _check_config_file _config_ask _sync_config_with_cache _echo_succ "Config syned to \`$config_file\` file." if [ -z "$1" ]; then build_target="all" else build_target="$1" fi _echo_info "Building..." cmake --build $cmake_build_dir --target $build_target --parallel $(nproc) if [ -z "$1" ]; then _echo_succ "Succeeded to build all targets" else _echo_succ "Succeeded to build \`$build_target\`" fi } clean() { _echo_info "Cleaning..." if [ -z "$1" ]; then if [ -f $cmake_cache_file ]; then rm -rf $cmake_build_dir _config_default cmake --build $cmake_build_dir --target clean-all rm -rf $cmake_build_dir _echo_succ "Succeeded to clean all targets" elif [ -d $cmake_build_dir ]; then rm -rf $cmake_build_dir $cmake_user_build_dir $cmake_kernel_build_dir $cmake_libc_build_dirs _echo_warn "Succeeded to clean all targets, but some object files may be left" else _echo_info "Nothing to clean" fi else if [ -f $cmake_cache_file ]; then cmake --build $cmake_build_dir --target $1-clean _echo_succ "Succeeded to clean \`$1\`" else _echo_err "Cannot clean \`$1\`, please try \`$self clean\` to clean all" fi fi } distclean() { clean _echo_info "Removing config file..." rm -rf $config_file _echo_succ "Succeeded to distclean" } _print_help() { echo -e "\ ${BOLD}USAGE:${NONE} ${BOLD}$self [options] [command]${NONE} ${BOLD}OPTIONS:${NONE} ${BOLD}--local, -l${NONE} run command in local environment (rather than docker) ${BOLD}COMMANDS:${NONE} Local: ${BOLD}help, --help, -h${NONE} print this help text ${BOLD}update-submodules${NONE} update Git submodules according to config file Local or Docker: ${BOLD}defconfig [platform]${NONE} generate default config (\`raspi3\` if \`platform\` is not specified) ${BOLD}config${NONE} run configuration step (interactively ask for config value if not set) ${BOLD}menuconfig${NONE} use TUI menu to edit config ${BOLD}build [target]${NONE} build the project (\`target\` can be \`libc\`, \`user\`, \`kernel\` or empty) ${BOLD}clean [target]${NONE} clean the project (\`target\` can be \`libc\`, \`user\`, \`kernel\` or empty) ${BOLD}distclean${NONE} clean the project and remove config file " } _update_submodules() { if [ -d $cmake_build_dir/local ]; then rm -rf $cmake_build_dir/local fi _echo_info "Configuring CMake in local environment..." cmake -B $cmake_build_dir/local -C $cmake_init_cache_abort _echo_info "Updating Git submodules..." cmake --build $cmake_build_dir/local --target update-submodules rm -rf $cmake_build_dir/local _echo_succ "Succeeded to update submodules" } _docker_run() { if [ -f /.dockerenv ]; then # we are already in docker container $@ else test -t 1 && use_tty="-t" docker run -i $use_tty --rm \ -u $(id -u ${USER}):$(id -g ${USER}) \ -v $(pwd):/chos -w /chos \ promisivia/treesls_chcore_builder:v2.2 \ $self $@ fi } _main() { run_in_docker=true while [ $# -gt 0 ]; do case $1 in help | --help | -h) _print_help exit ;; --local | -l) run_in_docker=false ;; -*) _echo_err "$self: invalid option \`$1\`\n" break ;; update-submodules) # need Git SSH key authentication, so must run in local env _update_submodules exit ;; *) if [[ "$1" == "_"* || $(type -t "$1") != function ]]; then _echo_err "$self: invalid command \`$1\`\n" break fi if [[ $run_in_docker == true ]]; then _docker_run $@ else $@ fi exit ;; esac shift done # no command is run _print_help exit 1 } _main $@