[CMake] Re: emitting CMakeLists.txt for TRY_COMPILE considered
harmful
Brandon J. Van Every
bvanevery at gmail.com
Thu Nov 23 02:02:08 EST 2006
Maybe the following is a better explanation of the pitfalls of emitting
a CMakeLists.txt.
This is taken from the current Chicken CMake build.
####################################################################
# CHANGELOG #
####################################################################
# A distribution archive (aka a tarball) will have ChangeLog already.
# A Darcs repository tree will not, however. We have to generate it.
#
# Note that as of Nov. 22nd, 2006, a Darcs package that understands
# Cygwin paths is not readily available. It is possible to compile
# Darcs from Haskell sources, but that requires GHC, which can be
# difficult to get working. The upshot is it's a PITA to access
# Darcs from Cygwin and hence to create a ChangeLog. It's possible,
# but one has to proceed carefully.
#
# Different shells can cause Darcs to fail. For instance, running a
# Windows native Darcs under a Cygwin shell can fail, because the
# Windows native Darcs doesn't understand Cygwin paths. A workaround
# is to avoid issuing any Cygwin path to Darcs, and instead use a
# WORKING_DIRECTORY, so that CMake handles some of its own paths and
# not Darcs.
#
# In principle, if we need to use a Darcs command, we should test
# whether Darcs is available and actually works. In practice, writing
# reliable tool tests in CMake 2.4.4 is painful.
#
# You cannot use EXECUTE_PROCESS to write a tool test. It executes in
# CMake's environment, not the actual build environment. For instance,
# let's say Darcs is available at the Windows command prompt.
# EXECUTE_PROCESS will say it works. However, it won't actually work
# under Visual Studio, because VS doesn't typically receive all the paths
# that the command prompt does.
#
# To write a tool test, Kitware expects one to emit a trivial CMakeLists.txt
# to a temporary subdirectory, and then TRY_COMPILE it. In practice, this
# approach is exceedingly fragile, due to quote consumption problems with
# FILE(WRITE ...) and with shells.
#
# What's really needed is an entirely different / better mechanism for
# tool testing. Something that's exactly parallel to the code we'd
# write here at the toplevel, so that there are no weird extraneous
# considerations.
SET(CHANGELOG_FILE -NOTFOUND)
IF(EXISTS ${Chicken_SOURCE_DIR}/_darcs)
FIND_PROGRAM(DARCS_EXE darcs)
IF(DARCS_EXE)
FILE(REMOVE_RECURSE ${CMAKE_CURRENT_BINARY_DIR}/try-darcs)
# Note the need to escape any quotes that are part of the file output.
# I cannot figure out how to get code emitted in a file to quote
properly.
# Consequently, we use a WORKING_DIRECTORY to duck the issue.
#
# Note that the \"${DARCS_EXE}\" quotes are necessary here, even though
# they are not generally necessary in the toplevel CMakeLists.txt, i.e.
# this file you're reading now. At this level, whitespace is
escaped, i.e.
# ${DARCS_EXE} = E:/Program\ Files/darcsdir-w32
# But once emitted, the whitespace escapes are lost. We get
# ${DARCS_EXE} = E:/Program Files/darcsdir-w32
# and of course Cygwin dies, as E:/Program isn't a valid command.
#
# Possibly this emission code should be replaced with a CONFIGURE_FILE
# template. Or else substitutions should be performed with
# STRING(CONFIGURE ...). Anything to get the quote / escape problems
# under control.
FILE(WRITE ${CMAKE_CURRENT_BINARY_DIR}/try-darcs/CMakeLists.txt "
PROJECT(try-darcs)
ADD_CUSTOM_TARGET(try
WORKING_DIRECTORY \"${Chicken_SOURCE_DIR}\"
COMMAND \"${DARCS_EXE}\" changes --last=0
)
")
TRY_COMPILE(DARCS_WORKS
${CMAKE_CURRENT_BINARY_DIR}/try-darcs
${CMAKE_CURRENT_BINARY_DIR}/try-darcs
try-darcs try
)
IF(DARCS_WORKS)
SET(CHANGELOG_FILE ${CMAKE_CURRENT_BINARY_DIR}/ChangeLog)
# Custom commands have the format:
#
# COMMAND command1 [args...]
#
# command1 has to be a CMake path. This is not documented in
CMake 2.4.4.
# Think of command1 as receiving "special interpretation" and not
really
# being a "custom" command, i.e. you're not free to do what you like.
#
# Also, if command1 is an absolute pathname, it must have whitespace
# handled properly. The results of CMake Find* commands always have
# whitespace handled properly, but if you're homebrewing your own
# pathnames, be careful.
#
# Other path statements in [args...] need to be paths that your
# tool understands. This means you'll need native paths, unless your
# tool happens to like CMake paths.
#
# As of November 22, 2006, there is no Cygwin version of Darcs.
# Instead, one typically has a Windows native version of
# Darcs running under a Cygwin shell. This Windows native Darcs
# does not understand Cygwin paths, i.e.
--repodir=/cygdrive/c/whatever
# will fail.
#
# Ideally, we'd complete the implementation of WINDOWS_PATH and
provide
# proper paths. Pragmatically, this is a PITA. We adopt the
expedient
# of using a working directory so that we don't have to bother with
# --repodir.
#
# stdout is redirected to the CHANGELOG_FILE. Redirection is a shell
# operation, so we need a path that the shell understands. Generally,
# keeping what a CMake comamnd1 needs, separate from what a tool
needs,
# separate from what a shell needs, is confusing. But that's the
drill.
NATIVE_PATH(CHANGELOG_FILE NATIVE_CHANGELOG_FILE)
# Always build the ChangeLog unconditionally. It doesn't take long,
# and we need to make sure it stays up to date. There is no easy
# way to determine if the Darcs repository has changed since we last
# looked at it, so brute force is the workaround.
ADD_CUSTOM_TARGET(darcs-changelog ALL
COMMENT "Generating ${NATIVE_CHANGELOG_FILE} from Darcs repository."
WORKING_DIRECTORY ${Chicken_SOURCE_DIR}
COMMAND ${DARCS_EXE} changes > ${NATIVE_CHANGELOG_FILE}
)
ELSE(DARCS_WORKS)
ECHO_TARGET(darcs-changelog "Darcs does not work. Cannot create
ChangeLog.")
ENDIF(DARCS_WORKS)
ELSE(DARCS_EXE)
ECHO_TARGET(darcs-changelog "Cannot find Darcs. Cannot create
ChangeLog.")
ENDIF(DARCS_EXE)
ELSE(EXISTS ${Chicken_SOURCE_DIR}/_darcs)
IF(EXISTS ${Chicken_SOURCE_DIR}/ChangeLog)
SET(CHANGELOG_FILE ${Chicken_SOURCE_DIR}/ChangeLog)
ECHO_TARGET(darcs-changelog "No Darcs repository. ChangeLog already
exists as part of archive distribution.")
ELSE(EXISTS ${Chicken_SOURCE_DIR}/ChangeLog)
ECHO_TARGET(darcs-changelog "No Darcs repository. ChangeLog is
missing from archive distribution.")
ENDIF(EXISTS ${Chicken_SOURCE_DIR}/ChangeLog)
ENDIF(EXISTS ${Chicken_SOURCE_DIR}/_darcs)
Brandon J. Van Every wrote:
> Summary: emitting a CMakeLists.txt is never parallel with standard
> coding practices in the toplevel CMakeLists.txt. At a minimum, this
> causes programmers to do everything 2 different ways. In the likely
> case, the emission fails because it is fragile as quotes are
> consumed. These kinds of problems can easily chew up a programmer's
> entire day, as I did today.
>
> I need to test whether Darcs source control commands actually work in
> build environments. It's dicey under Windows because there's no
> Cygwin version of Darcs, just a Windows native version. That would
> seem good for Windows people, but the Windows Command Prompt, Cygwin
> shell, MSYS shell, and the Visual Studio shell are all distinct build
> environments. They don't share the same search paths, and they don't
> understand each other's path conventions. So, aiming the right path
> at the right shell and tool is highly problematical.
>
> I have working code for this problem, targeted at CMake 2.4.3. In
> that version, I simply ducked all the problems. I used
> WORKING_DIRECTORY as much as possible so that I wouldn't have to use
> the Darcs --repodir=E:\devel\src\chicken command option. I resorted
> to such workarounds after 3 days of head scratching. The head
> scratching did have a productive outcome, however: Brad implemented
> the VERBATIM feature in reaction to my problem. Unfortunately, then I
> went into survival mode and was unable to allocate time to trying out
> VERBATIM while CMake 2.4.4 was still in beta.
>
> Come CMake 2.4.4, I thought I would give VERBATIM a whirl. Oddly, I
> was unable to get it to do anything constructive. I started having
> success when I abandoned it, at least at the level of my topmost
> CMakeLists.txt. Code like the following works fine:
>
> # NATIVE_PATH reverses slashes and adds quotes for Windows
> NATIVE_PATH(Chicken_SOURCE_DIR NATIVE_CHICKEN_SOURCE_DIR)
> NATIVE_PATH(CHANGELOG_FILE NATIVE_CHANGELOG_FILE)
>
> ADD_CUSTOM_TARGET(darcs-changelog ALL
> COMMENT "Generating ${NATIVE_CHANGELOG_FILE} from Darcs
> repository."
> COMMAND ${DARCS_EXE} changes
> --repodir=${NATIVE_CHICKEN_SOURCE_DIR} > ${NATIVE_CHANGELOG_FILE}
> )
>
> But I cannot, for the life of me, emit similar code via a FILE(WRITE
> ...).
> SOME_HAIRY_PATH(Chicken_SOURCE_DIR REPO)
> FILE(WRITE ${CMAKE_CURRENT_BINARY_DIR}/try-darcs/CMakeLists.txt "
> PROJECT(try-darcs)
> ADD_CUSTOM_TARGET(try
> COMMAND ${DARCS_EXE} changes --repodir=${REPO} --last=0
> )
> ")
>
> It doesn't matter what function I write for SOME_HAIRY_PATH. Doesn't
> matter whether I add quotes, a level of escapes, more backslashes,
> less backslashes, anything, everything, nothing. The problem is, the
> act of emitting a file in and of itself consumes quotes. I should
> have strings like --repodir=\"E:\devel\src\chicken\" in the above
> example. But that doesn't work because the single \ are interpreted
> as escapes for letters, giving CMake parse errors.
> --repodir=\"E:\\devel\\src\\chicken\" writes out to the file and
> doesn't generate parse errors, but the \\ aren't transformed into \,
> they stay as \\. So Darcs barfs on them.
>
> VERBATIM didn't seem to solve this. Did I miss how it was supposed to
> be used? Or was FILE(WRITE ...) consumption not considered?
>
>
> Cheers,
> Brandon Van Every
>
>
More information about the CMake
mailing list