libsgfc++ 2.0.1
A C++ library that uses SGFC to read and write SGF (Smart Game Format) data.
|
This document contains instructions on how to build, install and execute all parts of the project: The software library itself, the unit tests, a sample program and the API documentation.
The project requires CMake 3.10 or newer to build, and a reasonably modern version of Git to clone the project repository and fetch some Git submodules. Building the API documentation requires Doxygen.
Use your package manager of choice to install these components on your system. On macOS the recommended package manager is Homebrew, on Windows it's Chocolatey.
After cloning the repository you must first initialize its Git submodules:
cd /path/to/project git submodule update --init --recursive
Now you're ready to build. On platforms that support Makefile-based build systems these commands should do it:
mkdir build cd build # If you want a debug build then replace "Release" with "Debug" cmake -DCMAKE_BUILD_TYPE=Release .. cmake --build .
Building with Xcode or Visual Studio is explained in sections further down.
After building you can run tests from the build
folder with this command:
ctest
Why does ctest
report only a single test? This is because CMake sees the Catch2 test runner as a single test, when in fact the test runner is only the "facade" of the project's entire test suite. Execute the Catch2 test runner directly, with something like the following command, to see more detailed results. The command may vary on different platforms.
./test/libsgfcplusplus-test
The Catch2 test runner also has more capabilities than ctest
to control which tests should be run. Invoke the test runner with the -h
option to see all the options that are available. Example: All test scenarios are tagged, you can list those tags like this:
./test/libsgfcplusplus-test --list-tags
To run only tests marked with certain tags specify the tags in brackets. The following examples only runs tests that are related to text encodings and/or that excercise go-specific functionality:
./test/libsgfcplusplus-test "[encoding],[go]" # or ./test/libsgfcplusplus-test "[encoding][go]" # and
The [filesystem]
tag marks all tests that interact with the filesystem. To exclude these:
./test/libsgfcplusplus-test "~[filesystem]"
For more details see the Catch2 documentation.
The libsgfc++ build system integrates Catch2 by way of the CMake commands find_package()
or add_subdirectory()
. It attempts to locate the package in two steps:
find_package()
command.add_subdirectory()
command.If both lookup attempts fail, the build fails.
You can define the source distribution path where the build system should look for the package by defining the variable CATCH2_SOURCEDISTRIBUTION_PREFIX
:
cmake -DCMAKE_BUILD_TYPE=Release \ -DCATCH2_SOURCEDISTRIBUTION_PREFIX=/path/to/folder \ ..
If you don't define a source distribution path, the build system assumes test/Catch2
as the default source distribution path. This path points to the Catch2 Git submodule which you have initialized at the very beginning.
After the tests have run successfully you install the build products to a destination folder of your choice. CMake 3.15 and newer include a command line option --install
which can be used like this:
cmake --install . --prefix /path/to/destfolder
Older CMake versions don't have the --install
command line option and you must use the generated build system to perform the installation. You also have to define the prefix folder when you generate the build system. Example for make
:
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/path/to/destfolder .. [...] make install
The following things will be installed:
lib
: A shared library and a static library.include/libsgfcplusplus
: The library's public header files.Frameworks
: A shared library framework and a static library framework.lib/libsgfcplusplus
: CMake package files.A couple of notes:
#include <libsgfcplusplus/SgfcPlusPlusFactory.h>
.find_package()
. See the section "CMake support for downstream projects" below for more details.The library's source code is documented with Doxygen formatted inline comments. To generate the documentation you have to have Doxygen installed.
Generating and installing the API documentation is optional, and therefore is not part of the regular software build. These are the commands that need be invoked separately:
cd build cmake --build . --target libsgfcplusplus-doc cmake --install . --component libsgfcplusplus-doc --prefix /path/to/destfolder
Notes:
share/doc
. By default this consists of a set of HTML files.doc/CMakeLists.txt
. In that file you can set variables whose names directly correspond to tags in the Doxygen configuration file. For instance, if you want to have additional LaTeX output you can set DOXYGEN_GENERATE_LATEX
to YES
, this will set the corresponding Doxygen tag GENERATE_LATEX
to YES
.doc/CMakeLists.txt
so that Doxygen generates more output formats you also have to edit doc/SourcesList.cmake
to make sure that the additional output formats are installed. Set the OUTPUT_DIRECTORIES
variable accordingly.doc/SourcesList.cmake
and change the SOURCES
variable so that it does not include the src
subfolder. If you leave the configuration as it is, then you can still focus on just the public API docs by navigating to the "Public API" module in the generated documentation.The example
folder contains sample code that demonstrates how to use the library in a client application. When you build the project a simple command line program is also built that you can invoke in one of four modes to run the sample code.
The four modes quickly explained:
ISgfcCommandLine
.ISgfcDocumentReader
.ISgfcDocumentWriter
.The default build builds everything, but if you wish you can disable certain build products by setting the corresponding variables:
ENABLE_SHARED_LIBRARY
to NO
to prevent the shared library from being built.ENABLE_STATIC_LIBRARY
to NO
to prevent the static library from being built.ENABLE_SHARED_FRAMEWORK
to NO
to prevent the shared library framework from being built.ENABLE_STATIC_FRAMEWORK
to NO
to prevent the static library framework from being built.ENABLE_TESTS
to NO
to prevent unit tests from being built.ENABLE_EXAMPLES
to NO
to prevent the examples from being built.The following example builds everything except the static library framework and the examples:
cmake -DCMAKE_BUILD_TYPE=Release -DENABLE_STATIC_FRAMEWORK=NO -DENABLE_EXAMPLES=NO .. cmake --build .
You can also set the variable ENABLE_DEFAULT
to NO
as a quick way to change the default from "build everything" to "build nothing". After that you can enable only certain build products.
The following example only builds the shared library:
cmake -DCMAKE_BUILD_TYPE=Release -DENABLE_DEFAULT=NO -DENABLE_SHARED_LIBRARY=YES .. cmake --build .
In the previous section you have seen how to generate a Makefile-based build system. CMake can also generate an Xcode project, like this:
cd build cmake -G Xcode .. # Launch Xcode with the generated project open libsgfc++.xcodeproj
The ALL_BUILD
target in the Xcode project builds all targets, i.e. both the shared and the static libraries, the shared and static library frameworks, and the unit tests.
The RUN_TESTS
target builds the unit test target and executes a custom post-build script that is supposed to run the test. The post-build script currently doesn't seem to work. It is therefore recommended building the unit test target (libsgfc++-test
) and selecting "Product > Run" to execute the test runner. This always works, and Xcode is even nice enough to pop up an output pane that shows you the output of the command line test runner.
The ZERO_CHECK
target checks whether your CMakeLists.txt
files have changed and if necessary updates the Xcode project with the changes.
CMake can generate a Visual Studio solution like this:
cd build cmake -G "Visual Studio 16 2019" ..
If you use a different version of Visual Studio replace the -G
parameter value. Invoking just cmake -G
prints out available values.
When CMake has finished you can launch Visual Studio with the generated solution file libsgfc++.sln
.
The above generates a true "out of source" build system that is so typical for CMake. An alternative approach is shown in the article CMake projects in Visual Studio on Microsoft's official documentation site. This is a convenient GUI-only approach, but it generates stuff in two folders .vs
and out
in the project root.
Some packages need to be installed before building can start:
Once all packages are installed the Cygwin build works just like it does on other UNIX-like systems. Just follow the steps in the first sections at the top of this document.
No instructions available. This has not been tested yet.
Cross-compiling for iOS (or for any of the other Apple device OS's) currently seems to be a bit of a moving target in CMake, due to some changes that have been going on in the Apple developer ecosystem in the last years. Driving factors are the introduction of Silicon Macs (i.e. macOS machines running on ARM processors) and the continued evolution of Xcode (e.g. introduction of a new / removal of the old build system).
The next two sections therefore present two ways how to cross-compile for iOS: An old way, which will eventually stop working in future CMake versions (and probably does not even work today on Silicon Macs), and a new way, which may need refinement in the future but should at least work on all machines given modern versions of CMake and Xcode are used.
Both solutions build on CMake's basic mechanics for cross-compiling for iOS, which are explained in the cmake-toolchains
man page and are also available from the CMake website.
IOS_INSTALL_COMBINED
Here is a set of commands that should work with CMake 3.28.1, on an Intel macOS machine where Xcode 15 is installed. This solution uses the property IOS_INSTALL_COMBINED
, which has been deprecated in CMake version 3.28, so the solution will eventually stop working once CMake removes the property completely. Also note that the solution most likely does not work on Silicon Macs - if you have one of these machines then jump ahead to the "new way" section below.
cd /path/to/libsgfcplusplus mkdir build cd build cmake .. -G Xcode \ -DCMAKE_SYSTEM_NAME=iOS \ "-DCMAKE_OSX_ARCHITECTURES=arm64;x86_64" \ -DCMAKE_XCODE_ATTRIBUTE_ONLY_ACTIVE_ARCH=NO \ -DCMAKE_IOS_INSTALL_COMBINED=YES cmake --build . --config Release cmake --install . --prefix `pwd`/install
Notes:
-DCMAKE_BUILD_TYPE=Release
because the Xcode generator produces a multi-configuration build system which is capable of building both Debug and Release configurations. The configuration therefore must be selected during the --build
step.ENABLE_*
variables is set, the libsgfc++ project's CMake configuration internally decides to create only the static library and the static library framework. The reason is to avoid a codesigning error - see the section Codesigning when building for iOS for more details.x86_64
for the simulator build, and arm64
for the device build. The result of both builds will be stored in the same library files, making them so-called "universal" binaries. Use lipo -info /path/to/file
to check what's inside such a file. Omitting CMAKE_OSX_ARCHITECTURES
builds slices for all default architectures.ONLY_ACTIVE_ARCH
flag to NO
is important so that Xcode really builds those architectures we just mentioned. If we didn't set this Xcode would only build the architecture in the NATIVE_ARCH
Xcode build setting.IOS_INSTALL_COMBINED
to YES
causes the targets to be built for both the device SDK and the simulator SDK. An arm64
build made with the device SDK is slightly different than an arm64
build made with the simulator SDK. CMake figures out on its own which SDK it must use to build each architecture. The property also indicates to CMake that it needs to build all architectures in CMAKE_OSX_ARCHITECTURES
, not just the first, and to stitch together all resulting slices into a universal binary.--build
and the --install
steps are done separately, because only then does CMake work correctly with that new Xcode build system.-T buildsystem 1
to the generate CMake command), and it was also mandatory to combine the build and install steps, i.e. to have only a --build
step during which the option --target install
is also specified.--install
step, the pwd
command is used because CMake requires the installation prefix to be an absolute path.The results of the "old way" solution are universal (aka "fat") binaries: These are binaries that can contain multiple architectures (aka "slices") as long as they are different. On Intel macOS machines this is fine because the simulator build (x86_64
architecture) and the device build (arm64
architecture) have different architectures, so it is no problem to store both slices in the same universal binary. On ARM macOS machines (aka Silicon Macs) this does not work, though, because on these machines the simulator build and the device build both use the arm64
architecture.
Apple's solution for this is a new kind of framework called "XCFramework": This is an enhanced framework bundle (recognizable by the extension .xcframework
) that can contain any number of binaries for different platforms. It seems that currently CMake does not have built-in support for creating XCFrameworks, instead one has to stitch together an XCFramework bundle manually from individual framework parts using xcodebuild -create-xcframework
.
Here is an example that was tested on an Intel Mac, but should also work on a Silicon Mac. Note that this solution no longer uses the deprecated property IOS_INSTALL_COMBINED
Notes:
CMAKE_OSX_SYSROOT
variable selects the SDK to be used for building each platform. Specifying an SDK name without a version (e.g. iphonesimulator
instead of iphonesimulator17.2
) selects the latest SDK version. A list of available SDKs can be obtained by running xcodebuild -showsdks
.CMAKE_OSX_ARCHITECTURES
and let Xcode build slices for all default architectures of the target platform. On Silicon Macs this should only build only an arm64
slice for both platforms. On Intel Macs this builds only an arm64
slice for the device platform, and both arm64
and x86_64
slices for the simulator platform. -DCMAKE_OSX_ARCHITECTURES=x86_64
can be specified to avoid building two slices for the simulator.ENABLE_*
variables. This results in both the shared library and the shared library framework to be built, although we only need the framework for stitching the XCFramework. The overhead of building the unneeded shared library is negligible, though, because it consists of the same object files as the framework. To avoid building the shared library, the options -DENABLE_DEFAULT=NO
and -DENABLE_STATIC_FRAMEWORK=YES
can be added to the generate command.-G
command) without specifying the SDK, and then selecting the SDK during the --build
step with -sdk <sdk-name>
. The same documentation also gives a warning that taking this shortcut might cause problems with commands such as find_package()
or find_library()
. To be on the safe side our example therefore does not use the shortcut.If you see an Xcode build error "unable to attach DB" then you are likely affected by an incompatibility between the way how CMake handles IOS_INSTALL_COMBINED
and the Xcode build system. Depending on the combination of versions of Xcode and CMake that you have, if you still want to use IOS_INSTALL_COMBINED
you may therefore need to select the old or the new Xcode build system.
Some background information:
-T buildsystem=<value>
to a CMake generator command (a command using the -G
option) to select the Xcode build system to be used later on in the --build
step.For iOS builds the project default is to not build the shared library, shared library framework, unit tests or the examples. The reason is that when built for the device these binaries all require codesigning, but since the project does not provide a codesigning identity the build would just fail.
The following example shows how you can force the shared library to be built with a codesigning identity of your own.
cmake .. -G Xcode \ [...] # same options as shown in the cross-compiling section -DENABLE_SHARED_LIBRARY=YES \ -DCMAKE_XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY=<identity>
Note: You can find out your codesigning identy with this command: xcrun security find-identity -v -p codesigning
. It's the long hex string at the start of the output.
When you build libsgfc++ for iOS you may encounter errors because some build products require a certain minimum iOS version. Known cases:
std::filesystem
If you have these errors you have to set the deployment target to 13.0. The deployment target is set with the CMake option -DCMAKE_OSX_DEPLOYMENT_TARGET=13.0
.
Should you try to build unit tests or the examples for iOS you will encounter an obstacle: The unit test runner and the examples are both executables, so they need to be codesigned, but unlike a shared library an executable can only be codesigned if it has a bundle identifier.
The project does not provide any bundle identifiers, so if you insist on building the unit tests and/or the examples for iOS that is something you have to provide. There is currently no example how to build bundles - a starting point would be the CMake documentation for MACOSX_BUNDLE.
libsgfc++ exports its CMake targets (both from the build tree and from the install tree) as a CMake package. This allows downstream projects that also use CMake to generate their build system to easily integrate libsgfc++ into their own build. The CMake command for this is find_package()
.
If libsgfc++ has been installed to a system-wide installation path (e.g. /usr/local
) then it can be found without specifying a path. The search logic employed by find_package()
is exceedingly complicated and is not documented here. If you want to know more look it up in the CMake documentation of the find_package()
command.
An example:
find_package ( libsgfcplusplus 0.1 EXACT )
If libsgfc++ has been installed to some non-standard path, then the downstream project must provide a hint where to look for the CMake package files. The hint should be the installation prefix used to install libsgfc++, not the subfolder where the CMake package files actually reside. Again, find_package()
employs clever search logic to probe for the files.
Another example:
find_package ( libsgfcplusplus 0.1 EXACT HINT /path/to/installation-prefix )
As can be seen in the examples, the package name to use is libsgfcplusplus
. The exported targets all have the prefix libsgfcplusplus::
. The exported targets are:
libsgfcplusplus::libsgfcplusplus_shared
libsgfcplusplus::libsgfcplusplus_static
libsgfcplusplus::libsgfcplusplus_shared_framework
libsgfcplusplus::libsgfcplusplus_static_framework
To use the exported targets after find_package()
has successfully located the package, the downstream project typically specifies the exported target names in their target_link_libraries()
commands. Example:
target_link_libraries ( downstream-target-name libsgfcplusplus::libsgfcplusplus_shared )
This not only adds the link instructions (including to the iconv
dependency) to the downstream target, it also adds the INTERFACE include directories of the linked libsgfc++ target to the include directories of the downstream target. This way the downstream project can write #include
statements like this in their source code:
#include <libsgfcplusplus/SgfcPlusPlusFactory.h>