1# Copyright 2006-2008 The FLWOR Foundation.
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14#
15# Utility routines to package a local SVN working copy's changes into
16# a single file, and for applying that file to a fresh SVN enlistment
17# elsewhere.
18
19
20MACRO (FIND_PREREQS)
21  # Definitely need svn here.
22  FIND_PROGRAM(SVN_EXECUTABLE svn DOC "subversion command line client")
23  IF(NOT SVN_EXECUTABLE)
24    MESSAGE (FATAL_ERROR "Subversion is required; not found")
25  ENDIF(NOT SVN_EXECUTABLE)
26  SET (svn "${SVN_EXECUTABLE}")
27
28  # create path to execute zorba
29  SET(ZORBA_EXE "${ZORBA_BUILD_DIR}/bin/zorba")
30  IF (WIN32)
31    SET(ZORBA_EXE "${ZORBA_EXE}.bat")
32  ENDIF (WIN32)
33  IF (NOT EXISTS ${ZORBA_EXE})
34    MESSAGE (FATAL_ERROR "Zorba is required; not found. Specify -DZORBA_BUILD_DIR to point to your build directory if necessary. (${ZORBA_EXE})")
35  ENDIF (NOT EXISTS ${ZORBA_EXE})
36  EXECUTE_PROCESS (COMMAND "${ZORBA_EXE}" --omit-xml-declaration
37    --query "1+1" OUTPUT_VARIABLE ignored RESULT_VARIABLE result)
38  IF (result)
39    MESSAGE (FATAL_ERROR "Zorba is not functional. Specify -DZORBA_BUILD_DIR to point to your build directory if necessary. (${ZORBA_EXE})")
40  ENDIF (result)
41ENDMACRO (FIND_PREREQS)
42
43
44# Utility routine: given svn-status.xml file and a changelist (may be
45# ""), return a list of files which match any specified status.
46MACRO (get_files_with_status filelist svnstatusxml changelist)
47  # Which element to look at?
48  if ("${changelist}" STREQUAL "")
49    set (elem "target")
50  else ("${changelist}" STREQUAL "")
51    set (elem "changelist[@name=\"${changelist}\"]")
52  endif ("${changelist}" STREQUAL "")
53
54  # Which statuses to look at?
55  set (whereclause "")
56  set (prefix "where")
57  foreach (status ${ARGN})
58    set (whereclause "${whereclause}${prefix} $status eq \"${status}\"")
59    set (prefix " or")
60  endforeach (status ${ARGN})
61
62  # BUG: if the query below has new lines you will get a Zorba syntax error on Windows
63  set (query "fn:string-join(for $entry in status/${elem}/entry let $status := data($entry/wc-status/@item) ${whereclause} return $entry/@path, ';')")
64  # Set "filelist" to result
65  execute_process (COMMAND "${ZORBA_EXE}" --omit-xml-declaration
66                   --query "${query}" --context-item "${svnstatusxml}"
67                   OUTPUT_VARIABLE "${filelist}")
68ENDMACRO (get_files_with_status)
69
70
71# Args:
72#   srcdir: root of local SVN working copy with changes to package
73#   tmpdir: directory to hold temp files
74#   changelist: svn changelist to package (may be "" to package all changes)
75#   resultfile: Full path to changes.tgz file to create
76#
77# Note that this function will NOT create any output file if there are no
78# svn changes in the given srcdir.
79macro (svn_package srcdir tmpdir changelist resultfile)
80  find_prereqs ()
81
82  # Canonicalize srcdir
83  get_filename_component (abssrcdir "${srcdir}" ABSOLUTE)
84
85  # Start by making our working directory
86  set (chgdir "${tmpdir}/changes")
87  file (MAKE_DIRECTORY "${chgdir}")
88
89  # Write out the "svn info" and "svn status" information
90  execute_process (COMMAND "${svn}" --xml info "${abssrcdir}"
91                   OUTPUT_FILE "${chgdir}/svn-info.xml")
92  execute_process (COMMAND "${svn}" --xml status "${abssrcdir}"
93                   OUTPUT_FILE "${chgdir}/svn-status.xml")
94
95  # Also save the named of the changelist for the remote queue to use
96  file (WRITE "${chgdir}/changelist" "${changelist}")
97
98  # Check to see whether there are any changes to package
99  get_files_with_status (chgfiles "${chgdir}/svn-status.xml" "${changelist}"
100    added modified deleted)
101  if (chgfiles)
102    # Pick out path names for added and modified files in the
103    # specified changelist, or from the default changelist (<target>
104    # element) if no changelist specified
105    get_files_with_status (copyfiles "${chgdir}/svn-status.xml" "${changelist}"
106      added modified)
107
108    # Copy each modified or added file to a corresponding place in the
109    # directory hierarchy of the changes directory. Skip directories.
110    file (MAKE_DIRECTORY "${chgdir}/files")
111    foreach (filepath ${copyfiles})
112      if (NOT IS_DIRECTORY "${filepath}")
113        file (TO_CMAKE_PATH "${filepath}" filepath)
114        file (RELATIVE_PATH relpath "${abssrcdir}" "${filepath}")
115        get_filename_component (reldir "${relpath}" PATH)
116        file (MAKE_DIRECTORY "${chgdir}/files/${reldir}")
117        message ("Copying ${filepath} to ${chgdir}/files/${reldir}..")
118        execute_process (COMMAND "${CMAKE_COMMAND}" -E copy
119          "${filepath}" "${chgdir}/files/${reldir}")
120      endif (NOT IS_DIRECTORY "${filepath}")
121    endforeach (filepath)
122
123    # Package up changes/ directory
124    execute_process (COMMAND "${CMAKE_COMMAND}" -E tar czf
125      "${resultfile}" "changes"
126      WORKING_DIRECTORY "${tmpdir}")
127  endif (chgfiles)
128  file (REMOVE_RECURSE "${chgdir}")
129endmacro (svn_package)
130
131
132# Args:
133#   changefile - a changes.tgz file from package_svn
134#   outdir - directory to check out SVN into and apply changes - see below
135#   tmpdir - directory to temporarily unpack changefile into
136#   logfile - file to append log messages into
137#   result_var - if the variable named by "result_var" is non-zero or contains
138#     a message after svn_unpackage() returns, an error was encountered
139#   resultdir_var - the variable named by "resultdir_var" will be set to
140#     the path the package was unpackaged into
141#
142# Regarding "outdir": svn_unpackage() will place the results into a
143# subdir of outdir. If there is already a subdir of outdir that is an
144# svn checkout of the same URL, it will unpackage into that
145# directory. Otherwise, it will create a new subdir with the basename
146# of "changefile" minus the .tgz extension.
147function (svn_unpackage changefile outdir tmpdir logfile result_var
148    resultdir_var)
149  find_prereqs ()
150
151  # Unpack changes.tgz into working dir
152  execute_process (COMMAND "${CMAKE_COMMAND}" -E tar xzf "${changefile}"
153                   WORKING_DIRECTORY "${tmpdir}")
154  set (chgdir "${tmpdir}/changes")
155
156  # De-Windows all text files - this also is currently Linux-specific
157  # and depends on "dos2unix" existing.
158  file (GLOB_RECURSE textfiles
159        "${chgdir}/*.c" "${chgdir}/*.cpp" "${chgdir}/*.cxx"
160        "${chgdir}/*.h" "${chgdir}/*.hpp" "${chgdir}/*.hxx"
161        "${chgdir}/*.txt" "${chgdir}/*.cmake" "${chgdir}/*.conf"
162        "${chgdir}/*.xml" "${chgdir}/*.xq" "${chgdir}/*.txt"
163        "${chgdir}/*.res" "${chgdir}/*.spec" "${chgdir}/*.xqlib")
164  foreach (textfile ${textfiles})
165    execute_process (COMMAND dos2unix -k "${textfile}")
166  endforeach (textfile)
167
168  # Determine SVN URL and revision
169  execute_process (COMMAND "${ZORBA_EXE}" --omit-xml-declaration
170                   --query "data(info/entry/@revision)"
171                   --context-item "${chgdir}/svn-info.xml"
172                   OUTPUT_VARIABLE svnrev)
173  execute_process (COMMAND "${ZORBA_EXE}" --omit-xml-declaration
174                   --query "data(info/entry/url)"
175                   --context-item "${chgdir}/svn-info.xml"
176                   OUTPUT_VARIABLE svnroot)
177  # Special hack: Sourceforge has two URLs for everything, one http: and
178  # one https:. The remote queue checks out via http:, so we have to
179  # tweak the unpackaged URL here if it's https:.
180  if ("${svnroot}" MATCHES "^https:.*sourceforge.net")
181    string (REPLACE "https:" "http:" svnroot "${svnroot}")
182  endif ("${svnroot}" MATCHES "^https:.*sourceforge.net")
183  file (APPEND "${logfile}" "Checking out/updating from ${svnroot}...\n")
184
185  # Look through outdir for a subdir matching svnroot.
186  set (svndir)
187  file (GLOB subdirs "${outdir}/*")
188  foreach (subdir ${subdirs})
189    execute_process (COMMAND "${svn}" info --xml "${subdir}"
190      OUTPUT_FILE "${tmpdir}/tmpinfo.xml" ERROR_VARIABLE ignored
191      RESULT_VARIABLE result)
192    if (NOT result)
193      execute_process (COMMAND "${ZORBA_EXE}" --omit-xml-declaration
194        --query "data(info/entry/url)"
195        --context-item "${tmpdir}/tmpinfo.xml"
196        OUTPUT_VARIABLE subdir_svnroot)
197      file (REMOVE "${tmpdir}/tmpinfo.xml")
198      if (subdir_svnroot STREQUAL svnroot)
199        set (svndir "${subdir}")
200        break ()
201      endif (subdir_svnroot STREQUAL svnroot)
202    endif (NOT result)
203  endforeach (subdir)
204  if (NOT svndir)
205    # No matching URL found; use basename of input file
206    get_filename_component (basename "${changesfile}" NAME_WE)
207    set (svndir "${outdir}/${basename}")
208  endif (NOT svndir)
209  file (APPEND "${logfile}" "..outputting to ${svndir}\n")
210  set (${resultdir_var} "${svndir}" PARENT_SCOPE)
211
212  execute_process (COMMAND "${svn}" checkout
213                   -r "${svnrev}" "${svnroot}" "${svndir}"
214                   OUTPUT_VARIABLE svnlog ERROR_VARIABLE svnlog)
215  file (APPEND "${logfile}" ${svnlog})
216
217  # Copy modified/added files on top of svn directory
218  file (APPEND "${logfile}" "Copying added/modified files...\n")
219  execute_process (COMMAND "${CMAKE_COMMAND}" -E copy_directory
220                   "${chgdir}/files" "${svndir}")
221  file (APPEND "${logfile}" "Done copying.\n")
222
223  # Modified files are already all set. Process svn-status.xml to
224  # delete any files which were deleted on client's svn, as named in
225  # the client's changelist.
226  file (READ "${chgdir}/changelist" changelist)
227  get_files_with_status (deletefiles
228                         "${chgdir}/svn-status.xml" "${changelist}" deleted)
229  execute_process (COMMAND "${ZORBA_EXE}" --omit-xml-declaration
230                   --query "data(info/entry/@path)"
231                   --context-item "${chgdir}/svn-info.xml"
232                   OUTPUT_VARIABLE clientroot)
233  # clientroot and the filepaths below are absolute paths from the
234  # client, which might be Windows-style paths with drive letters. The
235  # following is a horrific hack which lets us get the remote path
236  # component no matter which platform they came from. (Note: This
237  # only works on Linux, which is the platform the remote queue runs
238  # on. If we ever have a Windows remote queue machine, this will
239  # die.)
240  # 1. Stick a leading / on them, so Linux thinks they're absolute.
241  # 2. Convert to "CMake paths", which have forward-slashes.
242  # 3. Do relative_path as normal.
243  file (TO_CMAKE_PATH "/${clientroot}" clientroot)
244  foreach (filepath ${deletefiles})
245    file (TO_CMAKE_PATH "/${filepath}" filepath)
246    file (RELATIVE_PATH relpath "${clientroot}" "${filepath}")
247    execute_process (COMMAND "${svn}" delete "${svndir}/${relpath}"
248      OUTPUT_VARIABLE output ERROR_VARIABLE output RESULT_VARIABLE result)
249    file (APPEND "${logfile}" "${output}")
250    if (result)
251      set (${result_var} ${result} PARENT_SCOPE)
252      return ()
253    endif ()
254  endforeach (filepath ${deletefiles})
255
256  # Now add any new files, using the local output of "svn status" to
257  # find "unversioned" files.  (We could read the svn-status.xml from
258  # the changes file, but when it is based on a changelist, it won't
259  # include directories to add.)
260  execute_process (COMMAND "${svn}" --xml status "${svndir}"
261                   OUTPUT_FILE "${chgdir}/svn-status.xml")
262  get_files_with_status (addfiles "${chgdir}/svn-status.xml" "" unversioned)
263  foreach (filepath ${addfiles})
264    execute_process (COMMAND "${svn}" add "${filepath}"
265      OUTPUT_VARIABLE output ERROR_VARIABLE output RESULT_VARIABLE result)
266    file (APPEND "${logfile}" "${output}")
267    if (result)
268      set (${result_var} ${result} PARENT_SCOPE)
269      return ()
270    endif ()
271  endforeach (filepath ${addfiles})
272
273  # Remove temporary unpackaged workingset
274  file (REMOVE_RECURSE "${chgdir}")
275
276endfunction (svn_unpackage)
277