On Sat, Jun 8, 2024 at 4:10 PM Timofey Zhakov <t...@chemodax.net> wrote: > > Hi all! > > When I was starting using and developing Subversion, I had a big > challenge to build it for the first time, especially because > Subversion itself and its dependencies have different build systems, > so this is a very complicated process. > > The build process can be improved by adding a generator for the CMake > build system. This can be easily implemented due to the extensibility > of the Subversion's gen-make. > > Some advantages of using CMake in Subversion: > - CMake is used by some Subversion dependencies, so there is nothing > else to learn. > - Great Visual Studio integration for build and tests. In addition, > there is a VSCode extension for CMake. > - CMake provides a very useful and simple system for finding dependencies. > - Better for build scripts and reproducibility of the build. > > The commands that would be needed to build Subversion, if you have all > dependencies installed: > > ``` > $ .\gen-make.py -t cmake > $ cmake -B out -DCMAKE_INSTALL_PREFIX=/path/to/deps > $ cmake --build out > ``` > > Additionally, there is a great tool, vcpkg, which can be used to build > dependencies. It is integrated with CMake. The following command can > be used to install dependencies: > > ``` > git clone https://github.com/microsoft/vcpkg > cd vcpkg > .\bootstrap-vcpkg.bat -disableMetrics > .\vcpkg.exe install apr apr-util expat zlib > ``` > > I am attaching a draft patch with the implementation of the CMake generator. > > What do you think?
Hello, I'm updating the patch with improved dependency searching, related to APR and APR Util, and some minor improvements. Additionally, I realized that I didn't implement the SQL header generation. I didn't notice it earlier because I had the headers after using the vcnet project files. I'll do it soon. -- Timofei Zhakov
Index: . =================================================================== --- . (revision 1918202) +++ . (working copy) Property changes on: . ___________________________________________________________________ Modified: svn:ignore ## -65,3 +65,5 ## .swig_pl_checked .swig_py_checked .swig_rb_checked +out +CMakeLists.txt Index: build/generator/gen_cmake.py =================================================================== --- build/generator/gen_cmake.py (nonexistent) +++ build/generator/gen_cmake.py (working copy) @@ -0,0 +1,199 @@ +import os +import stat +import sys +from build.generator.gen_make import UnknownDependency, _normstr +import ezt +import gen_base + +class _eztdata(object): + def __init__(self, **kw): + vars(self).update(kw) + +class cmake_target(): + def __init__(self, name: str, type: str, deps: str, libs: str, + sources, msvc_libs, msvc_export, msvc_static): + self.name = name + self.type = type + self.deps = deps + self.libs = libs + self.sources = sources + self.msvc_libs = msvc_libs + self.msvc_export = msvc_export + self.msvc_static = ezt.boolean(msvc_static) + + self.has_msvc_libs = ezt.boolean(len(msvc_libs) > 0) + +def get_target_type(target: gen_base.Target): + if isinstance(target, gen_base.TargetExe): + if target.install == "test" or target.install == "sub-test": + return "test" + else: + return "exe" + if isinstance(target, gen_base.TargetSWIG): + return "swig" + if isinstance(target, gen_base.TargetSWIGProject): + return "swig-project" + if isinstance(target, gen_base.TargetSWIGLib): + return "swig-lib" + if isinstance(target, gen_base.TargetLib): + return "lib" + else: + return str(type(target)) + +class Generator(gen_base.GeneratorBase): + _extension_map = { + ('exe', 'target'): '.exe', + ('exe', 'object'): '.obj', + ('lib', 'target'): '.dll', + ('lib', 'object'): '.obj', + ('pyd', 'target'): '.pyd', + ('pyd', 'object'): '.obj', + ('so', 'target'): '.so', + ('so', 'object'): '.obj', + } + + def __init__(self, fname, verfname, options=None): + gen_base.GeneratorBase.__init__(self, fname, verfname, options) + + def write(self): + install_deps = self.graph.get_deps(gen_base.DT_INSTALL) + + install_sources = self.get_install_sources() + + # ensure consistency between runs + install_deps.sort() + + targets = [] + ra_modules = [] + fs_modules = [] + + for target_ob in self.graph.get_sources(gen_base.DT_LINK, "ra-libs"): + name = target_ob.name + if name == "libsvn_ra_serf": + pass + else: + ra_modules.append(target_ob.name) + for target_ob in self.graph.get_sources(gen_base.DT_LINK, "fs-libs"): + name = target_ob.name + if name == "libsvn_fs_base": + pass + else: + fs_modules.append(target_ob.name) + + for target_ob in install_sources: + target_ob: gen_base.Target + + if isinstance(target_ob, gen_base.TargetScript): + # there is nothing to build + continue + + sources = [] + deps = [] + libs = [] + + for link_dep in self.graph.get_sources(gen_base.DT_LINK, target_ob.name): + if isinstance(link_dep, gen_base.TargetJava): + deps.append(link_dep.name) + elif isinstance(link_dep, gen_base.TargetLinked): + if link_dep.external_lib: + name = link_dep.name + if name == "ra-libs": + libs += ra_modules + elif name == "fs-libs": + libs += fs_modules + elif name in ["apriconv", + "apr_memcache", + "magic", + "intl", + "macos-plist", + "macos-keychain", + "sasl"]: + pass + else: + libs.append("external-" + name) + elif link_dep.external_project: + pass + else: + libs.append(link_dep.name) + elif isinstance(link_dep, gen_base.ObjectFile): + deps = self.graph.get_sources(gen_base.DT_OBJECT, + link_dep, + gen_base.SourceFile) + for dep in deps: + sources.append(dep.filename) + elif isinstance(link_dep, gen_base.HeaderFile): + pass + else: + raise UnknownDependency + + target_type = get_target_type(target_ob) + target_name = target_ob.name + + if target_type == "exe" or target_type == "lib" or target_type == "test": + msvc_libs = [] + for lib in target_ob.msvc_libs: + if lib == "setargv.obj": + pass + else: + msvc_libs.append(lib) + + msvc_export = [] + msvc_static = False + if isinstance(target_ob, gen_base.TargetLib): + for export in target_ob.msvc_export: + path = "subversion/include/" + export.replace("\\", "/") + msvc_export.append(path) + msvc_static = target_ob.msvc_static + + targets.append(cmake_target(target_name, target_type, + deps, libs, sources, + msvc_libs, msvc_export, msvc_static)) + + data = _eztdata( + targets = targets, + ) + + template = ezt.Template(os.path.join('build', 'generator', 'templates', + 'CMakeLists.txt.ezt'), + compress_whitespace=False) + template.generate(open('CMakeLists.txt', 'w'), data) + + def get_install_sources(self): + install_sources = self.graph.get_all_sources(gen_base.DT_INSTALL) + result = [] + + for target in install_sources: + target: gen_base.Target + + if not self.check_ignore_target(target): + result.append(target) + + result.sort(key = lambda s: s.name) + + return result + + def check_ignore_target(self, target: gen_base.Target): + ignore_names = [ + "libsvn_fs_base", + "libsvn_auth_gnome_keyring", + "libsvn_auth_kwallet", + "libsvn_ra_serf", + "libsvnxx", + "svnxx-tests", + "mod_dav_svn", + "mod_authz_svn", + "mod_dontdothat", + "__JAVAHL__", + "__JAVAHL_TESTS__", + "libsvnjavahl", + ] + + for name in ignore_names: + if target.name == name: + return True + + if isinstance(target, gen_base.TargetExe): + if target.install == "bdb-test": + return True + + return False Property changes on: build/generator/gen_cmake.py ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Index: build/generator/templates/CMakeLists.txt.ezt =================================================================== --- build/generator/templates/CMakeLists.txt.ezt (nonexistent) +++ build/generator/templates/CMakeLists.txt.ezt (working copy) @@ -0,0 +1,225 @@ +cmake_minimum_required(VERSION 3.5) + +project("Subversion") + +option(SVN_BUILD_PROGRAMS "Build Subversion programs (such as svn.exe)" ON) +option(SVN_BUILD_TEST "Build Subversion test-suite" OFF) + +option(BUILD_SHARED_LIBS "Build using shared libraries" OFF) +option(SVN_SQLITE_USE_AMALGAMATION "Use sqlite amalgamation" ON) +set(SVN_SQLITE_AMALGAMATION_DIR "${CMAKE_SOURCE_DIR}/sqlite-amalgamation" + CACHE STRING "Directory with sqlite amalgamation" +) +option(SVN_USE_INTERNAL_UTF8PROC "Use internal version of utf8proc" ON) +option(SVN_USE_INTERNAL_LZ4 "Use internal version of lz4" ON) + +find_package(Python COMPONENTS Interpreter REQUIRED) + +configure_file( + "${CMAKE_CURRENT_SOURCE_DIR}/subversion/svn_private_config.hw" + "${CMAKE_CURRENT_BINARY_DIR}/svn_private_config.h" +) + +set(SVN_INCLUDE_DIRECTORIES + "${CMAKE_CURRENT_SOURCE_DIR}/subversion/include" + "${CMAKE_CURRENT_BINARY_DIR}" +) + +if (WIN32) + add_compile_definitions( + "alloca=_alloca" + ) +endif() + +# APR config might not exist, so don't warn if it is not found. +# It will be handled later. +find_package(apr QUIET) + +if(apr_FOUND) + # It could rather APR-1 or APR-2 + message("Found APR config.") +else() + message("APR config not found. Looking for libraries and includes...") +endif() + +# 1. Look for shared APR-2 config then for its static version. +# 2. If APR-2 was not found, look for shared or static APR-1. +# 3. If APR config was not found, import the target manually. +# 4. Import APR-Util target manually, because it doesn't have a config. +if(TARGET apr::libapr-2) + add_library(external-apr ALIAS apr::libapr-2) + add_library(external-aprutil ALIAS apr::libapr-2) + message("Using shared APR-2 as APR and APR-Util in config mode.") +elseif(TARGET apr::apr-2) + add_library(external-apr ALIAS apr::apr-2) + add_library(external-aprutil ALIAS apr::apr-2) + message("Using static APR-2 as APR and APR-Util in config mode.") +else() + # APR + if(TARGET apr::libapr-1) + add_library(external-apr ALIAS apr::libapr-1) + message("Using shared APR-1 as APR in config mode.") + elseif(TARGET apr::apr-1) + add_library(external-apr ALIAS apr::apr-1) + message("Using static APR-1 as APR in config mode.") + else() + find_library(APR_LIBRARY + NAMES + libapr-1 + apr-1 + PATH_SUFFIXES lib + ) + + if(${APR_LIBRARY} STREQUAL "APR_LIBRARY-NOTFOUND") + message(FATAL_ERROR "APR library not found") + else() + message("Found APR library: ${APR_LIBRARY}") + endif() + + find_path(APR_INCLUDE_DIR + NAMES apr.h + PATH_SUFFIXES + include + include/apr-1 + include/apr-2 + ) + + if(${APR_INCLUDE_DIR} STREQUAL "APR_INCLUDE_DIR-NOTFOUND") + message(FATAL_ERROR "APR include dir not found") + else() + message("Found APR include dir: ${APR_LIBRARY}") + endif() + + add_library(external-apr STATIC IMPORTED) + + set_target_properties(external-apr PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES ${APR_INCLUDE_DIR} + IMPORTED_LOCATION ${APR_LIBRARY} + ) + endif() + + # APR-Util + find_library(APRUTIL_LIBRARY + NAMES + libaprutil-1 + aprutil-1 + PATHS ${CMAKE_PREFIX_PATH} + PATH_SUFFIXES lib + ) + + if(${APRUTIL_LIBRARY} STREQUAL "APRUTIL_LIBRARY-NOTFOUND") + message(FATAL_ERROR "APR-Util library not found") + else() + message("Found APR-Util library: ${APRUTIL_LIBRARY}") + endif() + + find_path(APRUTIL_INCLUDE_DIR + NAMES apu.h + PATHS ${CMAKE_PREFIX_PATH} + PATH_SUFFIXES + include + include/aprutil-1 # Not yet in apr + ) + + if(${APRUTIL_INCLUDE_DIR} STREQUAL "APRUTIL_INCLUDE_DIR-NOTFOUND") + message(FATAL_ERROR "APR-Util include dir not found") + else() + message("Found APR-Util include dir: ${APRUTIL_INCLUDE_DIR}") + endif() + + add_library(external-aprutil STATIC IMPORTED) + + set_target_properties(external-aprutil PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES ${APRUTIL_INCLUDE_DIR} + IMPORTED_LOCATION ${APRUTIL_LIBRARY} + ) +endif() + +find_package(ZLIB) +add_library(external-zlib ALIAS ZLIB::ZLIB) + +find_package(expat CONFIG REQUIRED) +add_library(external-xml ALIAS expat::expat) + +if(SVN_USE_INTERNAL_LZ4) + add_library(external-lz4 STATIC "build/win32/empty.c") + target_compile_definitions(external-lz4 PUBLIC "SVN_INTERNAL_LZ4") +else() + find_package(lz4 CONFIG REQUIRED) + add_library(external-lz4 ALIAS lz4::lz4) +endif() + +if(SVN_USE_INTERNAL_UTF8PROC) + add_library(external-utf8proc STATIC "build/win32/empty.c") + target_compile_definitions(external-utf8proc PUBLIC "SVN_INTERNAL_UTF8PROC") +else() + # TODO: + # find_package(utf8proc CONFIG REQUIRED) + # add_library(external-utf8proc ALIAS utf8proc) +endif() + +if(SVN_SQLITE_USE_AMALGAMATION) + add_library(external-sqlite STATIC "build/win32/empty.c") + target_include_directories(external-sqlite + PUBLIC "${SVN_SQLITE_AMALGAMATION_DIR}") + target_compile_definitions(external-sqlite PUBLIC SVN_SQLITE_INLINE) +else() + find_package(SQLite3 REQUIRED) + add_library(external-sqlite ALIAS SQLite::SQLite3) +endif() + +install(DIRECTORY "subversion/include/" DESTINATION "include/subversion-1") +[for targets][is targets.type "exe"] +if (SVN_BUILD_PROGRAMS) + add_executable([targets.name][for targets.sources] + [targets.sources][end] + ) + install(TARGETS [targets.name]) + [end][is targets.type "lib"] +if (TRUE) # TODO: + add_library([targets.name][if-any targets.msvc_static] STATIC[end][for targets.sources] + [targets.sources][end] + ) + if(BUILD_SHARED_LIBS) + set([targets.name]_HEADERS[for targets.msvc_export] + "[targets.msvc_export]"[end] + ) + add_custom_command( + WORKING_DIRECTORY + ${CMAKE_SOURCE_DIR} + COMMAND + ${Python_EXECUTABLE} + ARGS + "build/generator/extractor.py" + ${[targets.name]_HEADERS} + ">${CMAKE_BINARY_DIR}/[targets.name].def" + OUTPUT + "${CMAKE_BINARY_DIR}/[targets.name].def" + DEPENDS + "build/generator/extractor.py" + ${[targets.name]_HEADERS} + ) + target_sources([targets.name] PRIVATE "${CMAKE_BINARY_DIR}/[targets.name].def") + endif() + target_include_directories([targets.name] PUBLIC ${SVN_INCLUDE_DIRECTORIES}) + install(TARGETS [targets.name]) +[end][is targets.type "test"] +if(SVN_BUILD_TEST) + add_executable([targets.name][for targets.sources] [targets.sources][end]) + add_test([targets.name] [targets.name]) +[end] target_link_libraries([targets.name] PRIVATE[for targets.libs] + [targets.libs][end] + )[if-any targets.has_msvc_libs] + if(WIN32) + target_link_libraries([targets.name] PRIVATE[for targets.msvc_libs] [targets.msvc_libs][end]) + endif()[end] +endif() +[end] +if(SVN_BUILD_TEST) + enable_testing() +endif() + +# Enable globbing for all executables +if(MSVC) + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} setargv.obj") +endif() Index: gen-make.py =================================================================== --- gen-make.py (revision 1918202) +++ gen-make.py (working copy) @@ -49,6 +49,7 @@ gen_modules = { 'make' : ('gen_make', 'Makefiles for POSIX systems'), 'vcproj' : ('gen_vcnet_vcproj', 'VC.Net project files'), + 'cmake' : ('gen_cmake', 'CMake build system'), } def main(fname, gentype, verfname=None,