From a5138e4aaf28737a3b8020f03969109af10421b9 Mon Sep 17 00:00:00 2001 From: Jonah Beckford <71855677+jonahbeckford@users.noreply.github.com> Date: Thu, 10 Aug 2023 16:27:55 -0700 Subject: [PATCH] Do ./dk dkml.wrapper.upgrade --- cmake/FindDkToolScripts.cmake | 159 +++++++++++++++++++++++++++------- dk | 2 +- dk.cmd | 2 +- 3 files changed, 131 insertions(+), 32 deletions(-) diff --git a/cmake/FindDkToolScripts.cmake b/cmake/FindDkToolScripts.cmake index 988c92a..88ff391 100644 --- a/cmake/FindDkToolScripts.cmake +++ b/cmake/FindDkToolScripts.cmake @@ -1,5 +1,5 @@ # Recommendation: Place this file in source control. -# Auto-generated by `./dk dksdk.project.new` of DkHelloWorld. +# Auto-generated by `./dk dksdk.project.new` of dktool. include(FetchContent) @@ -10,7 +10,8 @@ function(parse_dktool_command_line) message(FATAL_ERROR "Missing . Usage: ./dk [args]") endif() set(command ${ARGV0}) - string(REPLACE "." "___" function_name ${command}) + string(REPLACE "." "___" expected_function_name ${command}) + message(VERBOSE "Searching for ${expected_function_name}") # Set policies (we are in a new EVAL CODE context) # Included scripts do automatic cmake_policy PUSH and POP @@ -18,6 +19,15 @@ function(parse_dktool_command_line) cmake_policy(SET CMP0011 NEW) endif() + # Parse the remainder of the arguments [args] + # * Use technique from [Professional CMake: A Practical Guide - Forwarding Command Arguments] + # to be able to forward arguments correctly to an inner function (the function). + cmake_parse_arguments(PARSE_ARGV 1 FWD "" "" "") + set(quotedArgs "") + foreach(arg IN LISTS FWD_UNPARSED_ARGUMENTS) + string(APPEND quotedArgs " [===[${arg}]===]") + endforeach() + # Setup the binary directory if(NOT DKTOOL_WORKDIR) message(FATAL_ERROR "Illegal state. Expecting DKTOOL_WORKDIR") @@ -25,56 +35,145 @@ function(parse_dktool_command_line) set(CMAKE_BINARY_DIR ${DKTOOL_WORKDIR}) set(CMAKE_CURRENT_BINARY_DIR ${CMAKE_BINARY_DIR}) - # Include all the user scripts - file(GLOB_RECURSE command_files LIST_DIRECTORIES FALSE + # Search in all the user scripts + set(dot_function_names) + file(GLOB_RECURSE command_files + LIST_DIRECTORIES FALSE + RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}/cmake/scripts cmake/scripts/*.cmake) foreach(command_file IN LISTS command_files) - include(${command_file}) + # Normalize and lowercase + cmake_path(NORMAL_PATH command_file) + string(TOLOWER "${command_file}" command_file) + cmake_path(REMOVE_EXTENSION command_file OUTPUT_VARIABLE command_file_no_ext) + + # Convert to list + string(REPLACE "/" ";" command_stems_no_namespace ${command_file_no_ext}) + + # Make a pretty description only for validation + set(pretty_stems ${command_stems_no_namespace}) + list(TRANSFORM pretty_stems PREPEND "'") + list(TRANSFORM pretty_stems APPEND "'") + string(JOIN ", " pretty_stems_str ${pretty_stems}) + string(REGEX REPLACE ",([^,]*)" " and \\1" pretty_stems_str "${pretty_stems_str}") + + # Validate that only alphanumeric with underscores (but not the reserved three underscores) + string(REGEX MATCH "[/a-z0-9_]*" only_alphanum_and_underscores "${command_file_no_ext}") + if(NOT only_alphanum_and_underscores STREQUAL "${command_file_no_ext}") + message(WARNING "Ignoring user script ${CMAKE_CURRENT_SOURCE_DIR}/${command_file}. +The stems of the user script (${pretty_stems_str}) must only contain letters, numbers and underscores.") + continue() + endif() + string(FIND "${command_file_no_ext}" "___" reserved_underscores) + if(reserved_underscores GREATER_EQUAL 0) + message(WARNING "Ignoring user script ${CMAKE_CURRENT_SOURCE_DIR}/${command_file}. +No stem of the user script (${pretty_stems_str}) can contain a triple underscore ('___').") + continue() + endif() + + # Translate dev/xxx.cmake to the "user" namespaced function name + # `user__dev__xxx` and `user.dev.xxx`. + set(command_stems ${command_stems_no_namespace}) + list(PREPEND command_stems "user") + string(JOIN "___" command_function_name ${command_stems}) + string(JOIN "." dot_function_name ${command_stems}) + list(APPEND dot_function_names ${dot_function_name}) + + # In a new scope (to avoid a global, leaky namespace) register the function. + message(VERBOSE "Shimming ${command_function_name}") + cmake_language(EVAL CODE " +function(${command_function_name}) + include(\"${CMAKE_CURRENT_SOURCE_DIR}/cmake/scripts/${command_file}\") + if(COMMAND run) + run(${quotedArgs}) + else() + message(FATAL_ERROR [[The user script ${CMAKE_CURRENT_SOURCE_DIR}/cmake/scripts/${command_file} was missing: + function(run) + # Your user code + endfunction() +]]) + endif() +endfunction() +") endforeach() # Include all the system scripts. # - Since the system scripts come after the user scripts, the user scripts # don't override the system scripts unless the user scripts use deferred - # hooks or redefine CMake built-in functions. + # hooks or redefine CMake built-in functions. Regardless, the user + # scripts are namespaced with `user__` prefix if(NOT IS_DIRECTORY cmake/scripts/dksdk) - # If this project (ex. DkHelloWorld) has the system scripts, it must + # If this project (ex. dktool) has the system scripts, it must # have all of them. Otherwise we download the system scripts. - FetchContent_Populate(DkHelloWorld + FetchContent_Populate(dktool QUIET - GIT_REPOSITORY https://gitlab.com/diskuv/samples/DkHelloWorld.git + GIT_REPOSITORY https://gitlab.com/diskuv/dktool.git GIT_TAG 1.0 ) - file(GLOB_RECURSE system_command_files LIST_DIRECTORIES FALSE - ${dkhelloworld_SOURCE_DIR}/cmake/scripts/dksdk/*.cmake) - list(APPEND command_files ${system_command_files}) + file(GLOB_RECURSE system_command_files + LIST_DIRECTORIES FALSE + RELATIVE ${dktool_SOURCE_DIR}/cmake/scripts + ${dktool_SOURCE_DIR}/cmake/scripts/dkml/*.cmake + ${dktool_SOURCE_DIR}/cmake/scripts/dksdk/*.cmake) foreach(command_file IN LISTS system_command_files) - include(${command_file}) + # Normalize and lowercase + cmake_path(NORMAL_PATH command_file) + string(TOLOWER "${command_file}" command_file) + cmake_path(REMOVE_EXTENSION command_file OUTPUT_VARIABLE command_file_no_ext) + + # Convert to list + string(REPLACE "/" ";" command_stems_no_namespace ${command_file_no_ext}) + + # Translate dksdk/xxx.cmake to the function name `dksdk__xxx` and `dksdk.xxx` + set(command_stems ${command_stems_no_namespace}) + string(JOIN "___" command_function_name ${command_stems}) + string(JOIN "." dot_function_name ${command_stems}) + list(APPEND dot_function_names ${dot_function_name}) + + # In a new scope (to avoid a global, leaky namespace) register the function. + message(VERBOSE "Shimming ${command_function_name}") + cmake_language(EVAL CODE " +function(${command_function_name}) + include(\"${dktool_SOURCE_DIR}/cmake/scripts/${command_file}\") + if(COMMAND run) + run(${quotedArgs}) + else() + message(FATAL_ERROR [[The system script ${dktool_SOURCE_DIR}/cmake/scripts/${command_file} was missing: + function(run) + # The system code + endfunction() +]]) + endif() +endfunction() +") + endforeach() endif() # Validate the exists - if(NOT COMMAND ${function_name}) - set(pretty_command_files ${command_files}) - list(TRANSFORM pretty_command_files PREPEND " ") - list(TRANSFORM pretty_command_files APPEND "\n") - string(JOIN "" str_pretty_command_files ${pretty_command_files}) - message(FATAL_ERROR "No command '${command}' exists. The function '${function_name}' was searched in the following locations: -${str_pretty_command_files}") + if(NOT COMMAND ${expected_function_name}) + set(pretty_function_names ${dot_function_names}) + list(TRANSFORM pretty_function_names PREPEND " ") + list(TRANSFORM pretty_function_names APPEND "\n") + string(JOIN "" str_pretty_function_names ${pretty_function_names}) + message(FATAL_ERROR "No command '${command}' exists. The following commands are available: +${str_pretty_function_names}") + message(FATAL_ERROR "No command '${command}' exists") endif() - # Parse the remainder of the arguments [args] - # * Use technique from [Professional CMake: A Practical Guide - Forwarding Command Arguments] - # to forward arguments correctly to an inner function (the function). - cmake_parse_arguments(PARSE_ARGV 1 FWD "" "" "") - set(quotedArgs "") - foreach(arg IN LISTS FWD_UNPARSED_ARGUMENTS) - string(APPEND quotedArgs " [===[${arg}]===]") - endforeach() - # Call the function - cmake_language(EVAL CODE "${function_name}(${quotedArgs})") + cmake_language(EVAL CODE "${expected_function_name}()") endfunction() +# DkML data home +if(WIN32) + set(DKML_DATA_HOME "$ENV{LOCALAPPDATA}/Programs/DiskuvOCaml") +elseif(DEFINED ENV{XDG_DATA_HOME}) + set(DKML_DATA_HOME "$ENV{XDG_DATA_HOME}/diskuv-ocaml") +else() + set(DKML_DATA_HOME "$ENV{HOME}/.local/share/diskuv-ocaml") +endif() + # DkSDK data home if(WIN32) set(DKSDK_DATA_HOME "$ENV{LOCALAPPDATA}/Programs/DkSDK") diff --git a/dk b/dk index 23df517..d84e696 100755 --- a/dk +++ b/dk @@ -1,6 +1,6 @@ #!/bin/sh # Recommendation: Place this file in source control. -# Auto-generated by `./dk dksdk.project.new` of DkHelloWorld. +# Auto-generated by `./dk dksdk.project.new` of dktool. # # Invoking: ./dk # That works in Powershell on Windows, and in Unix. Copy-and-paste works! diff --git a/dk.cmd b/dk.cmd index 5d413cc..aeefd72 100755 --- a/dk.cmd +++ b/dk.cmd @@ -1,6 +1,6 @@ @ECHO OFF REM Recommendation: Place this file in source control. -REM Auto-generated by `./dk dksdk.project.new` of DkHelloWorld. +REM Auto-generated by `./dk dksdk.project.new` of dktool. REM The canonical way to run this script is: ./dk REM That works in Powershell on Windows, and in Unix. Copy-and-paste works!