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



_______________________________________________
CMake mailing list
[email protected]
http://www.cmake.org/mailman/listinfo/cmake

Reply via email to