| # |
| # Copyright 2020, Data61, CSIRO (ABN 41 687 119 230) |
| # |
| # SPDX-License-Identifier: BSD-2-Clause |
| # |
| |
| # Try to set the cache dir based on environment variable, but override |
| # with value passed in as CMake -D argument. |
| set(cache_dir "$ENV{SEL4_CACHE_DIR}") |
| if(NOT ${SEL4_CACHE_DIR} STREQUAL "") |
| set(cache_dir "${SEL4_CACHE_DIR}") |
| endif() |
| # Convert to an absolute path. |
| if((NOT ("${cache_dir}" STREQUAL "")) AND NOT IS_ABSOLUTE cache_dir) |
| get_filename_component(cache_dir "${cache_dir}" ABSOLUTE BASE_DIR "${CMAKE_BINARY_DIR}") |
| endif() |
| set(MEMOIZE_CACHE_DIR "${cache_dir}" CACHE INTERNAL "" FORCE) |
| |
| # This function wraps a call to add_custom_command and may instead use an alternative cached |
| # copy if it can already find a version that has been built before. |
| # If git_directory is provided and the git directory has any uncommitted changes, then the |
| # cache is bypassed and will always build from source. |
| # key: A key to distinguish different memoized commands by, and also used in diagnostic output |
| # replace_dir: Directory to tar and cache. This tar'd directory is what gets expanded in cache hits. |
| # git_directory: A directory in a Git repository for performing clean/dirty check. |
| # extra_arguments: A string of extra arguments that if change invalidate previous cache entries. |
| # replace_files: Subset list of files to tar from replace_dir. If empty string then the whole dir |
| # will be cached. |
| function(memoize_add_custom_command key replace_dir git_directory extra_arguments replace_files) |
| |
| message(STATUS "Detecting cached version of: ${key}") |
| |
| # If a cache directory isn't set then call the underlying function and return. |
| if("${MEMOIZE_CACHE_DIR}" STREQUAL "") |
| message( |
| STATUS |
| " No cache path given. Set SEL4_CACHE_DIR to a path to enable caching binary artifacts." |
| ) |
| add_custom_command(${ARGN}) |
| return() |
| endif() |
| |
| set(dirty OFF) |
| |
| # Check if the git directory has any changes. |
| # Sets dirty to ON if there are changes |
| # sets git_source_hash to the git hash |
| if(NOT ${git_directory} STREQUAL "") |
| find_package(Git REQUIRED) |
| |
| execute_process( |
| COMMAND |
| "${GIT_EXECUTABLE}" diff --exit-code HEAD -- |
| WORKING_DIRECTORY "${git_directory}" |
| RESULT_VARIABLE res |
| OUTPUT_VARIABLE out |
| ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE |
| ) |
| if(res EQUAL 0) |
| |
| else() |
| set(dirty ON) |
| endif() |
| execute_process( |
| COMMAND "${GIT_EXECUTABLE}" rev-parse HEAD |
| WORKING_DIRECTORY "${git_directory}" |
| RESULT_VARIABLE res |
| OUTPUT_VARIABLE out |
| ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE |
| ) |
| set(git_source_hash ${out}) |
| endif() |
| |
| # Set the directory if no replace_files are set |
| if(replace_files STREQUAL "") |
| set(replace_files .) |
| endif() |
| |
| # Set the cache dir based on a hash of the git hash and extra arguments |
| # Note: We don't use the entire args to add_custom_command as inputs to the |
| # hash as they will change based on the path of the build directory. |
| # also it's too hard to guarantee that we can perfectly know whether a cache |
| # entry is valid or not, so delegate figuring out to the caller. |
| set(hash_string ${extra_arguments} ${git_source_hash}) |
| string(MD5 hash "${hash_string}") |
| set(cache_dir ${MEMOIZE_CACHE_DIR}/${key}/${hash}) |
| if(dirty) |
| # The git directory has changed, so don't use the cache |
| message(STATUS " ${key} is dirty - will build from source") |
| add_custom_command(${ARGN}) |
| else() |
| if(EXISTS ${cache_dir}/code.tar.gz) |
| # Cache hit. Create a different call to add_custom_command that |
| # merely unpacks the result from the last build instance into the |
| # target directory. |
| message(STATUS " Found valid cache entry for ${key}") |
| |
| # Set a cmake rebuild dependency on the cache file so if it changes |
| # cmake will get automatically rerun |
| set_property( |
| DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" |
| APPEND |
| PROPERTY CMAKE_CONFIGURE_DEPENDS "${cache_dir}/code.tar.gz" |
| ) |
| |
| # As we use the same output file we extract it from the args passed in. |
| if(NOT "${ARGV5}" STREQUAL "OUTPUT") |
| message(FATAL_ERROR "OUTPUT must be first argument to this function") |
| endif() |
| add_custom_command( |
| OUTPUT |
| # This has to match up with the OUTPUT variable of the call to add_custom_command |
| ${ARGV6} |
| # If we have to rebuild, first clear the temporary build directory as |
| # we have no correctly captured the output files or dependencies |
| COMMAND rm -r ${replace_dir} |
| COMMAND mkdir -p ${replace_dir} |
| COMMAND |
| tar -C ${replace_dir} -xf ${cache_dir}/code.tar.gz |
| DEPENDS ${deps} COMMAND_EXPAND_LISTS |
| COMMENT "Using cache ${key} build" |
| ) |
| else() |
| # Don't have a previous build in the cache. Create the rule but add |
| # some commands on the end to cache the result in our cache. |
| message(STATUS " Not found cache entry for ${key} - will build from source") |
| add_custom_command( |
| ${ARGN} |
| COMMAND mkdir -p ${cache_dir} |
| COMMAND |
| tar -zcf code.tar.gz -C ${replace_dir} ${replace_files} |
| COMMAND mv code.tar.gz ${cache_dir}/ |
| ) |
| endif() |
| endif() |
| endfunction() |