# # Wrapping # cmake_minimum_required (VERSION 3.5) project (CustomCommand) add_subdirectory(GeneratedHeader) # # Lib and exe path # if(NOT DEFINED bin_dir) set(bin_dir "bin") endif() set (LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/${bin_dir} CACHE INTERNAL "Single output directory for building all libraries.") set (EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/${bin_dir} CACHE INTERNAL "Single output directory for building all executables.") ################################################################ # # First test using a compiled generator to create a .c file # ################################################################ # add the executable that will generate the file add_executable(generator generator.cxx) ################################################################ # # Test using a wrapper to wrap a header file # ################################################################ # add the executable that will generate the file add_executable(wrapper wrapper.cxx) add_custom_command( OUTPUT ${PROJECT_BINARY_DIR}/wrapped.c ${PROJECT_BINARY_DIR}/wrapped_help.c DEPENDS wrapper MAIN_DEPENDENCY ${PROJECT_SOURCE_DIR}/wrapped.h COMMAND ${EXECUTABLE_OUTPUT_PATH}/${CMAKE_CFG_INTDIR}/wrapper ${PROJECT_BINARY_DIR}/wrapped.c ${PROJECT_BINARY_DIR}/wrapped_help.c ${CMAKE_CFG_INTDIR} # this argument tests passing of the configuration VERBATIM # passing of configuration should work in this mode ) ################################################################ # # Test creating files from a custom target # ################################################################ add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}//doc1.dvi # test 2 slashes DEPENDS ${PROJECT_SOURCE_DIR}/doc1.tex COMMAND ${CMAKE_COMMAND} ARGS -E copy ${PROJECT_SOURCE_DIR}/doc1.tex ${PROJECT_BINARY_DIR}/doc1.dvi ) add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/doc1.h COMMAND ${CMAKE_COMMAND} -E echo " Copying doc1.dvi to doc1temp.h." COMMAND ${CMAKE_COMMAND} -E copy ${PROJECT_BINARY_DIR}/doc1.dvi ${PROJECT_BINARY_DIR}/doc1temp.h ) add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/doc1.h APPEND DEPENDS ${PROJECT_BINARY_DIR}/doc1.dvi COMMAND ${CMAKE_COMMAND} -E echo " Copying doc1temp.h to doc1.h." COMMAND ${CMAKE_COMMAND} -E copy ${PROJECT_BINARY_DIR}/doc1temp.h ${PROJECT_BINARY_DIR}/doc1.h COMMAND ${CMAKE_COMMAND} -E echo " Removing doc1temp.h." COMMAND ${CMAKE_COMMAND} -E rm -f ${PROJECT_BINARY_DIR}/doc1temp.h ) # Add custom command to generate foo.h. add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/foo.h DEPENDS ${PROJECT_SOURCE_DIR}/foo.h.in COMMAND ${CMAKE_COMMAND} -E echo " Copying foo.h.in to foo.h." COMMAND ${CMAKE_COMMAND} -E copy ${PROJECT_SOURCE_DIR}/foo.h.in ${PROJECT_BINARY_DIR}/foo.h ) # Add the location of foo.h to the include path. include_directories(${PROJECT_BINARY_DIR}) # Test generation of a file to the build tree without full path. As # of CMake 2.6 custom command outputs specified by relative path go in # the build tree. add_custom_command( OUTPUT doc1.txt COMMAND ${CMAKE_COMMAND} -E echo "Example Document Target" > doc1.txt DEPENDS doc1.tex VERBATIM ) # Add a custom target to drive generation of doc1.h. add_custom_target(TDocument ALL COMMAND ${CMAKE_COMMAND} -E echo " Copying doc1.h to doc2.h." COMMAND ${CMAKE_COMMAND} -E copy ${PROJECT_BINARY_DIR}/doc1.h ${PROJECT_BINARY_DIR}/doc2.h DEPENDS doc1.txt ${PROJECT_BINARY_DIR}//doc1.h # test 2 slashes COMMENT "Running top-level TDocument commands" SOURCES doc1.tex ) # Setup a pre- and post-build pair that will fail if not run in the # proper order. add_custom_command( TARGET TDocument PRE_BUILD COMMAND ${CMAKE_COMMAND} -E echo " Writing doc1pre.txt." COMMAND ${CMAKE_COMMAND} -E copy ${PROJECT_SOURCE_DIR}/doc1.tex ${PROJECT_BINARY_DIR}/doc1pre.txt COMMENT "Running TDocument pre-build commands" ) add_custom_command( TARGET TDocument POST_BUILD COMMAND ${CMAKE_COMMAND} -E echo " Copying doc1pre.txt to doc2post.txt." COMMAND ${CMAKE_COMMAND} -E copy ${PROJECT_BINARY_DIR}/doc1pre.txt ${PROJECT_BINARY_DIR}/doc2post.txt BYPRODUCTS ${PROJECT_BINARY_DIR}/doc2post.txt COMMENT "Running TDocument post-build commands" ) # Setup a custom target that will fail if the POST_BUILD custom command # isn't run before it. add_custom_command( OUTPUT doc3post.txt DEPENDS ${PROJECT_BINARY_DIR}/doc2post.txt COMMAND ${CMAKE_COMMAND} -E echo " Copying doc2pre.txt to doc3post.txt." COMMAND ${CMAKE_COMMAND} -E copy ${PROJECT_BINARY_DIR}/doc2post.txt ${PROJECT_BINARY_DIR}/doc3post.txt COMMENT "Running TDocument post-build dependent custom command" ) add_custom_target(doc3Post ALL DEPENDS doc3post.txt) add_dependencies(doc3Post TDocument) ################################################################ # # Test using a multistep generated file # ################################################################ add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/foo.pre DEPENDS ${PROJECT_SOURCE_DIR}/foo.in TDocument # Ensure doc1.h generates before this target COMMAND ${CMAKE_COMMAND} ARGS -E copy ${PROJECT_SOURCE_DIR}/foo.in ${PROJECT_BINARY_DIR}/foo.pre ) add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/foo.c DEPENDS ${PROJECT_BINARY_DIR}/foo.pre COMMAND ${CMAKE_COMMAND} ARGS -E copy ${PROJECT_BINARY_DIR}/foo.pre ${PROJECT_BINARY_DIR}/foo.c ) # Test using OBJECT_DEPENDS to bring in a custom command. # Use a path that can be simplified to make sure paths # are consistently normalized. add_custom_command( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/subdir/../subdir/subdir.h COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/subdir.h.in ${CMAKE_CURRENT_BINARY_DIR}/subdir/subdir.h DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/subdir.h.in ) set_property(SOURCE ${PROJECT_BINARY_DIR}/foo.c PROPERTY OBJECT_DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/subdir/../subdir/subdir.h) # Add custom command to generate not_included.h, which is a header # file that is not included by any source in this project. This will # test whether all custom command outputs explicitly listed as sources # get generated even if they are not needed by an object file. add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/not_included.h DEPENDS ${PROJECT_SOURCE_DIR}/foo.h.in COMMAND ${CMAKE_COMMAND} -E copy ${PROJECT_SOURCE_DIR}/foo.h.in ${PROJECT_BINARY_DIR}/not_included.h ) # add the executable add_executable(CustomCommand ${PROJECT_BINARY_DIR}/foo.h ${PROJECT_BINARY_DIR}/foo.c ${PROJECT_BINARY_DIR}/wrapped.c ${PROJECT_BINARY_DIR}/wrapped_help.c ${PROJECT_BINARY_DIR}/generated.c ${PROJECT_BINARY_DIR}/not_included.h gen_redirect.c # default location for custom commands is in build tree ) # Add the rule to create generated.c at build time. This is placed # here to test adding the generation rule after referencing the # generated source in a target. add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/generated.c DEPENDS $<1:generator> $<0:does_not_exist> COMMAND generator ARGS ${PROJECT_BINARY_DIR}/generated.c ) target_link_libraries(CustomCommand GeneratedHeader) ############################################################################## # Test for using just the target name as executable in the COMMAND # section. Has to be recognized and replaced by CMake with the output # actual location of the executable. # Additionally the generator is created in an extra subdir after the # add_custom_command() is used. # # Test the same for add_custom_target() add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/generated_extern.cxx COMMAND generator_extern ${CMAKE_CURRENT_BINARY_DIR}/generated_extern.cxx ) add_executable(CustomCommandUsingTargetTest main.cxx ${CMAKE_CURRENT_BINARY_DIR}/generated_extern.cxx ) add_custom_target(RunTarget COMMAND generator_extern ${CMAKE_CURRENT_BINARY_DIR}/run_target.cxx ) add_custom_command(TARGET CustomCommandUsingTargetTest POST_BUILD COMMAND dummy_generator ${CMAKE_CURRENT_BINARY_DIR}/generated_dummy.cxx) add_subdirectory(GeneratorInExtraDir) ############################################################################## # Test shell operators in custom commands. add_executable(tcat tcat.cxx) # Test that list expansion from a generator expression works. set_property(TARGET tcat PROPERTY DEPSLIST tcat gen_redirect_in.c) add_custom_command(OUTPUT gen_redirect.c DEPENDS $ COMMAND tcat < ${CMAKE_CURRENT_SOURCE_DIR}/gen_redirect_in.c > gen_redirect.c COMMAND ${CMAKE_COMMAND} -E echo "#endif" >> gen_redirect.c VERBATIM ) ############################################################################## # Test non-trivial command line arguments in custom commands. set(EXPECTED_ARGUMENTS) set(CHECK_ARGS) if(NOT MSVC71) set(CHECK_ARGS -DPATH=c:/posix/path) endif() set(CHECK_ARGS ${CHECK_ARGS} c:/posix/path c:\\windows\\path 'single-quotes' single'quote \"double-quotes\" "\\;semi-colons\\;" "semi\\;colon" `back-ticks` back`tick "(parens)" "(lparen" "rparen)" {curly} {lcurly} rcurly} [square] [lsquare # these have funny behavior due to special cases for rsquare] # windows registry value names in list expansion $dollar-signs$ dollar$sign &ersands&x # Borland make does not like trailing ampersand one&ersand @two-ats@ one@at ~two-tilda~ one~tilda ^two-carrots^ one^carrot %two-percents% one%percent !two-exclamations! one!exclamation ?two-questions? one?question *two-stars* one*star =two+equals= one=equals _two-underscores_ one_underscore ,two-commas, one,comma .two-periods. one.period |two-pipes| one|pipe |nopipe "#two-pounds#" "one#pound" "#nocomment" "c:/posix/path/with space" "c:\\windows\\path\\with space" "'single quotes with space'" "single'quote with space" "\"double-quotes with space\"" "\\;semi-colons w s\\;" "semi\\;colon w s" "`back-ticks` w s" "back`tick w s" "(parens) w s" "(lparen w s" "rparen) w s" "{curly} w s" "{lcurly w s" "rcurly} w s" " w s" " w s" "[square] w s" "[lsquare w s" # these have funny behavior due to special cases for "rsquare] w s" # windows registry value names in list expansion "$dollar-signs$ w s" "dollar$sign w s" "&ersands& w s" "one&ersand w s" "@two-ats@ w s" "one@at w s" "~two-tilda~ w s" "one~tilda w s" "^two-carrots^ w s" "one^carrot w s" "%two-percents% w s" "one%percent w s" "!two-exclamations! w s" "one!exclamation w s" "*two-stars* w s" "one*star w s" "=two+equals= w s" "one=equals w s" "_two-underscores_ w s" "one_underscore w s" "?two-questions? w s" "one?question w s" ",two-commas, w s" "one,comma w s" ".two-periods. w s" "one.period w s" "|two-pipes| w s" "one|pipe w s" "#two-pounds# w s" "one#pound w s" ~ ` ! @ \# $ % ^ & _ - + = : "\;" \" ' , . ? "(" ")" { } [] ) if(NOT MINGW) # * # MinGW programs on windows always expands the wildcard! # / # MSys make converts a leading slash to the mingw home directory list(APPEND CHECK_ARGS * /) endif() # The windows command shell does not support a double quote by itself: # double\"quote # without messing up quoting of arguments following it. # Make tools need help with escaping a single backslash # \ # at the end of a command because they think it is a continuation # character. # We now have special cases for shell operators: # | < > << >> &> 2>&1 1>&2 # to allow custom commands to perform redirection. foreach(arg ${CHECK_ARGS} "") set(ARG "${arg}") string(REPLACE "\\" "\\\\" ARG "${ARG}") string(REPLACE "\"" "\\\"" ARG "${ARG}") string(APPEND EXPECTED_ARGUMENTS " \"${ARG}\", ") endforeach() configure_file(${CMAKE_CURRENT_SOURCE_DIR}/check_command_line.c.in ${CMAKE_CURRENT_BINARY_DIR}/check_command_line.c @ONLY) add_executable(check_command_line ${CMAKE_CURRENT_BINARY_DIR}/check_command_line.c) set(output_name "check_command_line") set_property(TARGET check_command_line PROPERTY OUTPUT_NAME ${output_name}) # set_target_properties(check_command_line PROPERTIES # COMPILE_FLAGS -DCHECK_COMMAND_LINE_VERBOSE) add_custom_command( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/command_line_check COMMAND ${CMAKE_COMMAND} -DMARK_FILE=${CMAKE_CURRENT_BINARY_DIR}/check_mark.txt -P ${CMAKE_CURRENT_SOURCE_DIR}/check_mark.cmake COMMAND ${EXECUTABLE_OUTPUT_PATH}/${CMAKE_CFG_INTDIR}/${output_name} ${CHECK_ARGS} "" VERBATIM COMMENT "Checking custom command line escapes (single'quote)" ) set_source_files_properties(${CMAKE_CURRENT_BINARY_DIR}/command_line_check PROPERTIES SYMBOLIC 1) add_custom_target(do_check_command_line ALL DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/command_line_check COMMAND ${CMAKE_COMMAND} -E echo "Checking custom target command escapes" COMMAND ${EXECUTABLE_OUTPUT_PATH}/${CMAKE_CFG_INTDIR}/${output_name} ${CHECK_ARGS} "" VERBATIM COMMENT "Checking custom target command line escapes ($dollar-signs$)" ) add_dependencies(do_check_command_line check_command_line) add_custom_target(pre_check_command_line COMMAND ${CMAKE_COMMAND} -E rm -f ${CMAKE_CURRENT_BINARY_DIR}/check_mark.txt ) add_dependencies(do_check_command_line pre_check_command_line) # # # Add a custom target called "SameName" -- then add a custom command in a # different target whose output is a full-path file called "SameName" -- then # add a second custom target that depends on the full-path file ".../SameName" # # At first, this reproduces a bug reported by a customer. After fixing it, # having this test here makes sure it stays fixed moving forward. # add_custom_command( OUTPUT SameName1.txt COMMAND ${CMAKE_COMMAND} -E touch SameName1.txt ) add_custom_target(SameName ALL DEPENDS SameName1.txt ) add_custom_command( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/subdir/SameName COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_CURRENT_BINARY_DIR}/subdir COMMAND ${CMAKE_COMMAND} -E touch ${CMAKE_CURRENT_BINARY_DIR}/subdir/SameName ) add_custom_target(DifferentName ALL DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/subdir/SameName ) # # # Per-config target name and generator expressions. add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../PerConfig PerConfig) add_custom_command( OUTPUT perconfig.out COMMAND ${PerConfig_COMMAND} DEPENDS ${PerConfig_DEPENDS} VERBATIM ) set_property(SOURCE perconfig.out PROPERTY SYMBOLIC 1) add_custom_target(perconfig_target ALL COMMAND ${CMAKE_COMMAND} -E echo "perconfig=$" "config=$" DEPENDS perconfig.out) # Test SOURCES in add_custom_target() with COMPILE_DEFINITIONS # which previously caused a crash in the makefile generators. add_custom_target(source_in_custom_target SOURCES source_in_custom_target.cpp) set_property(SOURCE source_in_custom_target PROPERTY COMPILE_DEFINITIONS "TEST" ) set(gen_path "${CMAKE_CURRENT_BINARY_DIR}//./foo") set(gen_file "${gen_path}/foo.cxx") add_custom_command( OUTPUT "${gen_file}" # Make sure the output directory exists before trying to write to it. COMMAND ${CMAKE_COMMAND} -E make_directory "${gen_path}" COMMAND ${CMAKE_COMMAND} -E touch "${gen_file}" ) add_library(NormOutput "${gen_file}") string(APPEND gen_path "/bar") set(gen_file "${gen_path}/bar.cxx") add_custom_command( OUTPUT "${gen_path}" COMMAND ${CMAKE_COMMAND} -E make_directory "${gen_path}" ) add_custom_command( OUTPUT "${gen_file}" DEPENDS "${gen_path}" COMMAND ${CMAKE_COMMAND} -E touch "${gen_file}") add_library(NormDepends "${gen_file}") # Test that USES_TERMINAL is parsed correctly. # It seems much more difficult to test that USES_TERMINAL actually gives # the subprocess console access, as test output is piped through CTest, # and CTest itself might not be connected to the console. set(gen_file "${gen_path}/bar2.cxx") add_custom_command( OUTPUT "${gen_file}" DEPENDS "${gen_path}" COMMAND ${CMAKE_COMMAND} -E touch "${gen_file}" VERBATIM USES_TERMINAL ) add_library(UseConsole "${gen_file}") add_custom_target(UseConsoleTarget ALL COMMAND ${CMAKE_COMMAND} -E echo "Custom console target." VERBATIM USES_TERMINAL ) if(CMAKE_XCODE_BUILD_SYSTEM VERSION_GREATER_EQUAL 12) # Xcode's "new build system" does not support multiple targets # producing the same custom command output. add_custom_target(GenPath DEPENDS "${gen_path}") add_dependencies(NormDepends GenPath) add_dependencies(UseConsole GenPath) endif() # Test COMMAND_EXPAND_LISTS set(cmp_args "1ARG=COMMAND_EXPAND_LISTS" "2ARG=test" "3ARG=outfile" "4ARG=content") set(AARGS "") foreach(arg IN LISTS cmp_args) list(APPEND AARGS "-DA${arg}") endforeach() set(gen_file "expand_custom_command.phony") add_custom_command( OUTPUT "${gen_file}" COMMAND ${CMAKE_COMMAND} ${AARGS} "-DB$,;-DB>" "-P" "${CMAKE_CURRENT_SOURCE_DIR}/compare_options.cmake" COMMAND_EXPAND_LISTS VERBATIM ) set_property(SOURCE "${gen_file}" PROPERTY SYMBOLIC ON) add_custom_target(command_expand_lists ALL DEPENDS "${gen_file}") set_property(TARGET command_expand_lists PROPERTY CMPARGS "${cmp_args}") # This also tests that `./` is squeezed out of the resulting path. set(depends_path "./depended_upon_path.txt") add_custom_command( OUTPUT ${depends_path} COMMAND ${CMAKE_COMMAND} -E touch ${depends_path} ) add_custom_command( OUTPUT "depends_on_path.txt" COMMAND ${CMAKE_COMMAND} -E touch "depends_on_path.txt" DEPENDS ${depends_path} ) add_custom_target(depends_on_path ALL DEPENDS "depends_on_path.txt") add_custom_command( OUTPUT "depends_on_in_source_path.txt" COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/main.cxx" depends_on_in_source_path.txt DEPENDS main.cxx ) add_custom_target(depends_on_in_source_path ALL DEPENDS "depends_on_in_source_path.txt") add_custom_command( OUTPUT "depends_on_in_rel_source_path.txt" COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/main.cxx" depends_on_in_rel_source_path.txt DEPENDS ./main.cxx ) add_custom_target(depends_on_in_rel_source_path ALL DEPENDS "depends_on_in_rel_source_path.txt") add_library(mac_fw SHARED mac_fw.c) set_target_properties(mac_fw PROPERTIES FRAMEWORK 1 LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/lib ) add_custom_command(OUTPUT mac_fw.txt COMMAND ${CMAKE_COMMAND} -E touch mac_fw.txt DEPENDS mac_fw) add_custom_target(drive_mac_fw ALL DEPENDS mac_fw.txt) # Test empty COMMANDs are omitted add_executable(empty_command empty_command.cxx) add_custom_command(TARGET empty_command POST_BUILD COMMAND $<0:date>)