Skip to content

Commit

Permalink
Add ./dk
Browse files Browse the repository at this point in the history
  • Loading branch information
Jonah Beckford committed Sep 3, 2023
1 parent 1e9ee11 commit 6adc3bc
Show file tree
Hide file tree
Showing 3 changed files with 1,123 additions and 0 deletions.
229 changes: 229 additions & 0 deletions cmake/FindDkToolScripts.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
##########################################################################
# File: dktool/cmake/FindDkToolScripts.cmake #
# #
# Copyright 2023 Diskuv, Inc. #
# #
# Licensed under the Open Software License version 3.0 #
# (the "License"); you may not use this file except in compliance #
# with the License. You may obtain a copy of the License at #
# #
# https://opensource.org/license/osl-3-0-php/ #
# #
##########################################################################

# Recommendation: Place this file in source control.
# Auto-generated by `./dk dksdk.project.new` of dktool.

include(FetchContent)

function(parse_dktool_command_line)
# The first argument is <command>. All dots will be replaced with a
# triple underscore as a convenience and to be pretty for the user.
# However, we do not error if no <command> is given ... we'll do
# that later.
set(command)
set(expected_function_name)
set(quotedArgs "")
if(ARGC EQUAL 0 OR (ARGC EQUAL 1 AND ARGV0 STREQUAL HELP))
message(NOTICE [[Usage:
./dk <command> HELP
./dk <command> [args]
]])
else()
set(command ${ARGV0})
string(REPLACE "." "___" expected_function_name ${command})
message(VERBOSE "Searching for ${expected_function_name}")

# 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 <command> function).
cmake_parse_arguments(PARSE_ARGV 1 FWD "" "" "")
foreach(arg IN LISTS FWD_UNPARSED_ARGUMENTS)
string(APPEND quotedArgs " [===[${arg}]===]")
endforeach()
endif()

# Set policies (we are in a new EVAL CODE context)
# Included scripts do automatic cmake_policy PUSH and POP
if(POLICY CMP0011)
cmake_policy(SET CMP0011 NEW)
endif()
# Allow GIT_SUBMODULES empty to mean no submodules
if(POLICY CMP0097)
cmake_policy(SET CMP0097 NEW)
endif()

# Setup the binary directory
if(NOT DKTOOL_WORKDIR)
message(FATAL_ERROR "Illegal state. Expecting DKTOOL_WORKDIR")
endif()
set(CMAKE_BINARY_DIR ${DKTOOL_WORKDIR})
set(CMAKE_CURRENT_BINARY_DIR ${CMAKE_BINARY_DIR})

# 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)
# 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. Regardless, the user
# scripts are namespaced with `user__` prefix
if(NOT IS_DIRECTORY cmake/scripts/dksdk)
# If this project (ex. dktool) has the system scripts, it must
# have all of them. Otherwise we download the system scripts.
FetchContent_Populate(dktool
QUIET
GIT_REPOSITORY https://gitlab.com/diskuv/dktool.git
GIT_TAG 1.0
# As of 3.25.3 the bug https://gitlab.kitware.com/cmake/cmake/-/issues/24578
# has still not been fixed. That means empty strings get removed.
# ExternalProject_Add(GIT_SUBMODULES) in dktool-subbuild/CMakeLists.txt
# means fetch all submodules.
# https://gitlab.kitware.com/cmake/cmake/-/issues/20579#note_734045
# has a workaround.
GIT_SUBMODULES cmake # Non-git-submodule dir that already exists
GIT_SUBMODULES_RECURSE OFF
)
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)
# 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()

# Pretty function names that are available
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})

# Exit if no <command>
if(NOT command)
message(NOTICE "The following commands are available:
${str_pretty_function_names}")
return()
endif()

# Validate the <command> exists
if(NOT COMMAND ${expected_function_name})
message(FATAL_ERROR "No command '${command}' exists. The following commands are available:
${str_pretty_function_names}")
message(FATAL_ERROR "No command '${command}' exists")
endif()

# Call the <command> function
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")
elseif(DEFINED ENV{XDG_DATA_HOME})
set(DKSDK_DATA_HOME "$ENV{XDG_DATA_HOME}/dksdk")
else()
set(DKSDK_DATA_HOME "$ENV{HOME}/.local/share/dksdk")
endif()

# Splat DKTOOL_CMDLINE
cmake_language(EVAL CODE "parse_dktool_command_line(${DKTOOL_CMDLINE})")
Loading

0 comments on commit 6adc3bc

Please sign in to comment.