[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