[CMake] Target visibility in CMake using Xcode generator

Michael Hertling mhertling at online.de
Sun Apr 10 20:33:52 EDT 2011


On 04/09/2011 06:32 PM, Manuel Holtgrewe wrote:
>> There're two spots in your A/CMakeLists.txt catching my eye,  
>> although I
>> doubt that they are actually causing the difficulties you report on:
>>
>> 1) The DEPENDS clause of ADD_CUSTOM_TARGET() is meant for file-level
>>   dependencies only, i.e. you shouldn't denote a target as target_aa
>>   but files and particularly OUTPUTs of custom commands as target_ab
>>   after that clause. Instead, you should use ADD_DEPENDENCIES() to
>>   establish a dependency of target_a on target_aa.
>> 2) When dealing with custom commands' OUTPUTs, it's best to specify
>>   full paths since it is hardly documented how relative paths are
>>   interpreted, i.e. relative to the source directory or the build
>>   tree. AFAIK, it's ADD_CUSTOM_COMMAND(OUTPUT ...) only that - in
>>   a documented manner - interprets a relative OUTPUT path w.r.t.
>>   to CMAKE_CURRENT_BINARY_DIR.
>>
>> Having said that, with a Makefile generator, your projects A and B
>> indeed work as one would usually expect, despite the limitation of
>> the ADD_CUSTOM_TARGET() command's DEPENDS clause mentioned in the
>> documentation, but perhaps, the Xcode generator or Xcode itself
>> is pickier.
> 
> I changed the CMakeLists.txt files as can bee seen at the bottom of  
> the email. I hope this resolves both points you described above.  
> However, the problem persists.
> 
> Is this the limitation you mention: "Dependencies listed with the  
> DEPENDS argument may reference files and outputs of custom commands  
> created with add_custom_command() in the same directory  
> (CMakeLists.txt file)."? [...]

Yes, in connection with "Use ADD_DEPENDENCIES to add dependencies *to*
or from other targets." from ADD_CUSTOM_TARGET()'s documentation, too.

> [...] The way I understand it, my  
> "add_custom_target()" statement references only files generated in the  
> same CMakeLists.txt. [...]

You might set the [RUNTIME_]OUTPUT_NAME property of your target_aa,
and on Windows, the target's default output is named target_aa.exe,
i.e. you cannot rely on your target_aa to produce an equally named
output file. Hence, if you have a "DEPENDS target_aa" clause for a
custom target, CMake would need to know that "target_aa" refers to
a logical target instead of an ordinary file, and this exceeds the
documented limitation. However, as stated previously, it seems to
work anyway - even with a RUNTIME_OUTPUT_NAME set to an arbitrary
value - so perhaps a CMake developer could tell us whether that
limitation is to be taken seriously or if it's overly strict.

> [...] The documentation does not explicitely limit a  
> target from another CMakeLists.txt file depending indirectly on  
> generated files.

Absolutely, but this indirect dependency must be conveyed by a
custom target as you've correctly done in your CMakeLists.txt.

> To clarify whether the problem is in Xcode or the generator, I grepped  
> for target_ab in a build directory for Xcode and one for Makefiles.  
> The results can also be found at the bottom of the email. As can be  
> seen, target_ab does not occur in combination with target_b. [...]

This is expected since target_ab - as a custom command's OUTPUT - can
not be referenced from another CMakeLists.txt file, in particular not
target_b's CMakeLists.txt. The crucial line for target_ab is rather:

./CMakeFiles/target_a.dir/build.make:target_a: target_ab

Nevertheless, after the first build, CMake's dependency scanner for
Makefiles has recognized the file-level dependency of target_b on
target_ab due to the "#include" statement as can be seen in
./B/CMakeFiles/target_b.dir/depend.make:

> B/CMakeFiles/target_b.dir/target_b.cpp.o: B/../target_ab

I.e. target_b gets rebuilt when the file "target_ab" has changed.

> [...] I also  
> grepped for "target_a" (the results are ommitted for their longness)  
> and target_a does not occur in any files related to target_b either.  

No, the related line for the Makefiles is in ./CMakeFiles/Makefile2:

> B/CMakeFiles/target_b.dir/all: CMakeFiles/target_a.dir/all

The dependency chain for target_b is mainly the following:

> target_b built from top-level Makefile
> builds target_b in CMakeFiles/Makefile2
> depends on B/CMakeFiles/target_b.dir/rule
> builds B/CMakeFiles/target_b.dir/all in CMakeFiles/Makefile2
> (1) depends on CMakeFiles/target_a.dir/all
>     (1.1) depends on CMakeFiles/target_aa.dir/all
>           builds CMakeFiles/target_aa.dir/build in CMakeFiles/target_aa.dir/build.make
>           depends on target_aa
>           depends on CMakeFiles/target_aa.dir/target_aa.cpp.o
>           depends on ../target_aa.cpp
>           (COMPILE)
>     (1.2) builds CMakeFiles/target_a.dir/build in CMakeFiles/target_a.dir/build.make
>           depends on target_a
>           depends on target_ab
>           (TOUCH)
> (2) builds B/CMakeFiles/target_b.dir/build in B/CMakeFiles/target_b.dir/build.make
>     depends on B/target_b
>     depends on B/CMakeFiles/target_b.dir/target_b.cpp.o
>     depends on ../B/target_b.cpp
>     (COMPILE)

Starting from directory B, target_b's dependencies originate as

> target_b built from B/Makefile
> depends on B/CMakeFiles/target_b.dir/rule
> builds B/CMakeFiles/target_b.dir/rule in CMakeFiles/Makefile2

so the remaining dependency chain is the same as the above-noted one.

> So it appears that the Xcode generator does not realize that I want it  
> to build target_a when building target_b in the B.xcodeproj file.

While this works with Makefiles, I could imagine that the Xcode
generator doesn't allow to build a (sub)project from within its
directory if there're references to the parent project, especially
targets. Since I don't have access to an Xcode installation at the
moment, I cannot investigate this issue in further detail. BTW, is
there a problem/badness/disadvantage to build target_b within the
parent project, i.e. from A.xcodeproj?

Regards,

Michael

> The question now is whether there is a way to tell the generator to do  
> what I want. If it is not possible to do so: Would extending the  
> generator to do what I want be correct or am I asking for something  
> that should not be supported?
> 
> Bests,
> Manuel
> 
> $ cat CMakeLists.txt
> cmake_minimum_required(VERSION 2.8)
> project(A)
> add_executable(target_aa target_aa.cpp)
> add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/target_ab  
> COMMAND touch ${CMAKE_CURRENT_BINARY_DIR}/target_ab)
> add_custom_target(target_a DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/ 
> target_ab)
> add_dependencies(target_a target_aa)
> add_subdirectory(B)
> 
> $ cat B/CMakeLists.txt
> cmake_minimum_required(VERSION 2.8)
> project(B)
> include_directories(${CMAKE_CURRENT_BINARY_DIR})
> add_executable(target_b target_b.cpp)
> add_dependencies(target_b target_a)
> 
> Makefiles $ grep -Ri target_ab .
> ./B/CMakeFiles/target_b.dir/CXX.includecache:../target_ab
> ./B/CMakeFiles/target_b.dir/CXX.includecache:/Users/manuel/Development/ 
> Sandbox/CMakeSandbox/A/B/../target_ab
> ./B/CMakeFiles/target_b.dir/CXX.includecache:B/../target_ab
> ./B/CMakeFiles/target_b.dir/depend.internal: B/../target_ab
> ./B/CMakeFiles/target_b.dir/depend.make:B/CMakeFiles/target_b.dir/ 
> target_b.cpp.o: B/../target_ab
> ./CMakeFiles/CMakeRuleHashes.txt:eb772089b1414de60dae83cdf775c8ef  
> target_ab
> ./CMakeFiles/target_a.dir/build.make:CMakeFiles/target_a: target_ab
> ./CMakeFiles/target_a.dir/build.make:target_ab:
> ./CMakeFiles/target_a.dir/build.make:	@$(CMAKE_COMMAND) -E  
> cmake_echo_color --switch=$(COLOR) --blue --bold "Generating target_ab"
> ./CMakeFiles/target_a.dir/build.make:	touch target_ab
> ./CMakeFiles/target_a.dir/build.make:target_a: target_ab
> ./CMakeFiles/target_a.dir/cmake_clean.cmake:  "target_ab"
> 
> Xcode $ grep -Ri target_ab .
> ./CMakeScripts/target_a_cmakeRulesBuildPhase.makeDebug:	/Users/manuel/ 
> Development/Sandbox/CMakeSandbox/A-build/Xcode/target_ab
> ./CMakeScripts/target_a_cmakeRulesBuildPhase.makeDebug:/Users/manuel/ 
> Development/Sandbox/CMakeSandbox/A-build/Xcode/target_ab:
> ./CMakeScripts/target_a_cmakeRulesBuildPhase.makeDebug:	touch target_ab
> ./CMakeScripts/target_a_cmakeRulesBuildPhase.makeMinSizeRel:	/Users/ 
> manuel/Development/Sandbox/CMakeSandbox/A-build/Xcode/target_ab
> ./CMakeScripts/target_a_cmakeRulesBuildPhase.makeMinSizeRel:/Users/ 
> manuel/Development/Sandbox/CMakeSandbox/A-build/Xcode/target_ab:
> ./CMakeScripts/target_a_cmakeRulesBuildPhase.makeMinSizeRel:	touch  
> target_ab
> ./CMakeScripts/target_a_cmakeRulesBuildPhase.makeRelease:	/Users/ 
> manuel/Development/Sandbox/CMakeSandbox/A-build/Xcode/target_ab
> ./CMakeScripts/target_a_cmakeRulesBuildPhase.makeRelease:/Users/manuel/ 
> Development/Sandbox/CMakeSandbox/A-build/Xcode/target_ab:
> ./CMakeScripts/target_a_cmakeRulesBuildPhase.makeRelease:	touch  
> target_ab
> ./CMakeScripts/target_a_cmakeRulesBuildPhase.makeRelWithDebInfo:	/ 
> Users/manuel/Development/Sandbox/CMakeSandbox/A-build/Xcode/target_ab
> ./CMakeScripts/target_a_cmakeRulesBuildPhase.makeRelWithDebInfo:/Users/ 
> manuel/Development/Sandbox/CMakeSandbox/A-build/Xcode/target_ab:
> ./CMakeScripts/target_a_cmakeRulesBuildPhase.makeRelWithDebInfo:	touch  
> target_ab


More information about the CMake mailing list