diff --git a/.github/linux_build.sh b/.github/linux_build.sh new file mode 100644 index 0000000..09c553a --- /dev/null +++ b/.github/linux_build.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +# Create build directory and enter it +mkdir bin +cd bin +# Create internal build and install directory, then enter it +mkdir build +mkdir install +cd build + +# Build in Release mode +cmake -DCMAKE_BUILD_TYPE=Release ../.. +cmake --build . +cmake --install . --prefix=../install + +# Back to root directory +cd .. +cd .. diff --git a/.github/windows_build.bat b/.github/windows_build.bat new file mode 100644 index 0000000..e4e22c7 --- /dev/null +++ b/.github/windows_build.bat @@ -0,0 +1,18 @@ +@ECHO OFF + +:: Create build directory and enter it +MKDIR bin +CD bin +:: Create internal build and install directory, then enter it +MKDIR build +MKDIR install +CD build + +:: Build with x64 architecture in Release mode +cmake -A x64 ../.. +cmake --build . --config Release +cmake --install . --prefix=../install --config Relese + +:: Back to root directory +CD .. +CD .. diff --git a/COMPILE.md b/COMPILE.md index a6daa74..aa80a15 100644 --- a/COMPILE.md +++ b/COMPILE.md @@ -11,40 +11,64 @@ It means that it is not stable and work in progress. * CMake 3.23 at least. * The common compiler supporting C++ 23 (GCC / Clang / MSVC). * Iconv (Optional on Windows. Required on other systems). -* [GoogleTest](https://github.com/google/googletest) (Required if you build test). +* [Google Test](https://github.com/google/googletest) (Required if you build test). +* [Google Benchmark](https://github.com/google/benchmark) (Required if you build benchmark). * Doxygen (Required if you build documentation). -* Python and Astral UV (Required if you use "User Build" method) + +If you are just want to build this project to make something works, or build other project, rather than code with it, +you commonly do not need build test, benchmark and documentation. +So you actually do not need Google Test, Google Benchmark and Doxygen. + +## Preparing + +### Compiler > [!WARNING] > You may face some issues when building on macOS with Clang. That's not your fault. > Clang used libc++ library lacks some essential features used by this project. > You may try other solutions for compiling this project on macOS or with Clang. -## Preparing +### Google Test -### GoogleTest - -GoogleTest is required if you need to build test. +Google Test is required if you need to build test. If you don't need this please skip this chapter. -We use GoogleTest v1.17.0. +We use Google Test v1.17.0. It would be okey use other versions but I have not test on them. > [!WARNING] -> When building this project, you may face link error with GoogleTest, especially on Linux. +> When building this project, you may face link error with Google Test, especially on Linux. > This issue is caused by that the binary provided by your package manager is built in C++17 and its ABI is incompatible with C++23. > See this [GitHub Issue](https://github.com/google/googletest/issues/4591) for more infomation. -> The solution is that download GoogleTest source code and build it in C++23 on your own. +> The solution is that download Google Test source code and build it in C++23 on your own. > Following content tell you how to do this. -There are the steps instructing you how to compile GoogleTest manually. +There are the steps instructing you how to compile Google Test manually. -1. Download GoogleTest source code with given version in GitHub Release page. +1. Download Google Test source code with given version in GitHub Release page. 1. Extract it into a directory. 1. Enter this directory and create 2 subdirectory `build` and `install` for CMake build and install respectively. 1. Enter `build` directory and configure CMake with extra `-DCMAKE_CXX_STANDARD=23 -Dgtest_force_shared_crt=ON` parameters. -1. Use CMake to build GoogleTest -1. Use CMake to install GoogleTest into previous we created `install` directory. +1. Use CMake to build Google Test +1. Use CMake to install Google Test into previous we created `install` directory. + +### Google Benchmark + +Google Benchmark is required if you need to build benchmark. +If you don't need this please skip this chapter. + +We use Google Benchmark v1.9.4. +It would be okey use other versions but I have not test on them. + +There are the steps instructing you how to compile Google Benchmark manually. + +1. Download Google Benchmark source code with given version in GitHub Release page. +1. Extract it into a directory. +1. Enter this directory and create link named `googletest` to previous fetched Google Test root directory. This is instructed by official manual because Google Benchmark rely on Google Test. Link can be create by executing `mklink /D googletest ` on Windows or `ln -s googletest` on POSIX-like OS. +1. Keep stay in this directory and create 2 subdirectory `build` and `install` for CMake build and install respectively. +1. Enter `build` directory and configure CMake with extra `-DCMAKE_CXX_STANDARD=23 -DBENCHMARK_ENABLE_TESTING=OFF` parameters. +1. Use CMake to build Google Benchmark +1. Use CMake to install Google Benchmark into previous we created `install` directory. ### Iconv @@ -71,33 +95,33 @@ So before compiling, you must make sure `doxygen` are presented in your environm ## Build and Install There are 2 different ways to build this project. -If you are the user of this project (just want this project to make something work), please choose "User Build". +If you are the user of this project (just want this project to make something works, or build other projects), please choose "User Build". If you are a developer (developer of this project, or use this project as dependency to develop your project), please choose "Developer Build". ### User Build "User Build" is basically how GitHub Action build this project. -We use Python 3.11 and UV 0.7.17 to manage our build script generator. -It would be okey use other versions but I have not test on them. - -TODO... +Execute `.github/windows_build.bat` on Windows or `.github/linux_build.sh` on POSIX-like OS (Linux and macOS) under **the root directory** of this project. The final built artifact is under `bin/install` directory. ### Developer Build -TODO... - -There is a list listing all variables you may configure during compiling. +First, there is a list listing all variables you may configure during compiling. * `YYCC_BUILD_TEST`: Set it to `ON` to build test. `OFF` in default. It is useful for the developer of this project. It also suit for the user who has runtime issues on their platforms to check whether this project works as expected. +If you are debugging this project to find bug, I suggest that you build this project under Debug mode and use this test project for debugging. +* `YYCC_BUILD_BENCHMARK`: Set it to `ON` to build benchmark. `OFF` in default. +It is useful for the developer of this project to checking the performace for those homemade functions. +It is highly suggested build this project with Release mode to have real benchmark result. * `YYCC_BUILD_DOC`: Set it to `ON` to build documentation. `OFF` in default. It may be useful for the developer who firstly use this project in their own projects. Please note that generated documentation is different in different platforms. * `YYCC_ENFORCE_ICONV`: Set it to `ON` to enable Iconv feature forcely. `OFF` in default. The usage of this option has been introduced in previous "Iconv" chapter. * `GTest_ROOT`: TODO +* `benchmark_ROOT`: TODO * `Iconv_ROOT`: TODO * `CMAKE_CXX_STANDARD`: Set C++ standard version of project. `23` in default and this version can not be lower than C++23. diff --git a/benchmark/yycc/string/op.cpp b/benchmark/yycc/string/op.cpp index f3b2cff..d62e817 100644 --- a/benchmark/yycc/string/op.cpp +++ b/benchmark/yycc/string/op.cpp @@ -7,12 +7,12 @@ using namespace std::literals::string_view_literals; namespace yyccbench::string::op { - static void StringStrip(benchmark::State& state) { + static void BM_StringStrip(benchmark::State& state) { std::u8string_view strl = u8" \thello\r\n"sv, words = u8" \t\r\n"sv; for (auto _ : state) { auto rv = OP::strip(strl, words); } } - BENCHMARK(StringStrip); + BENCHMARK(BM_StringStrip)->Name("StringStrip"); } diff --git a/script/.gitignore b/script/.gitignore index 8877f27..2edd436 100644 --- a/script/.gitignore +++ b/script/.gitignore @@ -2,10 +2,6 @@ # Exclude VSCode .vscode/ -# Exclude generated files -win_build.bat -linux_build.sh - ## ===== Python ===== # Byte-compiled / optimized / DLL files __pycache__/ diff --git a/script/gen_build_script.py b/script/gen_build_script.py deleted file mode 100644 index 3f9ea26..0000000 --- a/script/gen_build_script.py +++ /dev/null @@ -1,106 +0,0 @@ -import argparse -import typing -import re -import shlex -from pathlib import Path -from dataclasses import dataclass -import jinja2 - -def validate_cpp_ver(ver: str) -> str: - if re.match(r'^[0-9]+$', ver) is not None: return ver - else: raise argparse.ArgumentTypeError('invalid version of C++ standard.') - -def write_line(f: typing.TextIO, val: str) -> None: - f.write(val) - f.write('\n') - -# Reference: https://stackoverflow.com/questions/29213106/how-to-securely-escape-command-line-arguments-for-the-cmd-exe-shell-on-windows -def escape_for_cmd_exe(arg): - meta_re = re.compile(r'([()%!^"<>&|])') - return meta_re.sub('^\1', arg) -def escape_cmd_argument(arg): - if not arg or re.search(r'(["\s])', arg): - arg = '"' + arg.replace('"', r'\"') + '"' - return escape_for_cmd_exe(arg) -def escape_sh_argument(arg): - return shlex.quote(arg) - -@dataclass(frozen=True) -class ScriptSettings: - pic: bool - cpp_version: str - build_doc: bool - build_testbench: bool - -class TemplateRender: - loader: jinja2.BaseLoader - environment: jinja2.Environment - - win_template: jinja2.Template - linux_template: jinja2.Template - - settings: ScriptSettings - - def __init__(self, settings: ScriptSettings) -> None: - self.loader = jinja2.FileSystemLoader(self.__get_dir()) - self.environment = jinja2.Environment(loader=self.loader) - - self.win_template = self.environment.get_template('win_build.bat.jinja') - self.linux_template = self.environment.get_template('linux_build.sh.jinja') - - self.settings = settings - - def __get_dir(self) -> Path: - return Path(__file__).resolve().parent - - def __escape_path(self, val: str, is_win: bool) -> str: - if is_win: return escape_cmd_argument(val) - else: return escape_sh_argument(val) - - def __render(self, template: jinja2.Template, dest_file: str, is_win: bool) -> None: - with open(self.__get_dir() / dest_file, 'w', encoding='utf-8') as f: - f.write(template.render( - repo_root_dir = self.__escape_path(str(self.__get_dir().parent), is_win), - cpp_version = self.settings.cpp_version, - build_doc = self.settings.build_doc, - pic = settings.pic - )) - - def render_win_script(self) -> None: - self.__render(self.win_template, 'win_build.bat', True) - - def render_linux_script(self) -> None: - self.__render(self.linux_template, 'linux_build.sh', False) - - -if __name__ == '__main__': - # parse argument - parser = argparse.ArgumentParser( - prog='YYCC Windows Build Script Generator', - description='YYCC Windows Build Script Generator' - ) - parser.add_argument( - '-c', '--cpp', - action='store', default='17', dest='cpp', type=validate_cpp_ver, - help='The version of C++ standard used when building.' - ) - parser.add_argument( - '-d', '--build-doc', - action='store_true', dest='build_doc', - help='Build YYCC with documentation.' - ) - parser.add_argument( - '-p', '--pic', - action='store_true', dest='pic', - help='Enable Position Independent Code flag on non-Windows platform. This is crucial for compiling dynamic library using this library.' - ) - args = parser.parse_args() - - # build settings - settings = ScriptSettings(args.cpp, args.build_doc, args.pic) - # build template render and render result - render = TemplateRender(settings) - render.render_win_script() - render.render_linux_script() - - diff --git a/script/linux_build.sh.jinja b/script/linux_build.sh.jinja deleted file mode 100644 index 74fa05f..0000000 --- a/script/linux_build.sh.jinja +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/bash -# Navigate to project root directory -cd {{ repo_root_dir }} - -# Create build and install directory -mkdir build -mkdir install - -# Build as release version -cd build -cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_STANDARD={{ cpp_version }} {{ '-DCMAKE_POSITION_INDEPENDENT_CODE=True' if pic }} {{ '-DYYCC_BUILD_DOC=ON' if build_doc }} {{ '-DYYCC_BUILD_TESTBENCH=ON' if build_testbench }} ../.. --fresh -cmake --build . -cmake --install . --prefix ../install - -# Exit to original path -cd .. -echo "YYCC Linux CMake build done" diff --git a/script/win_build.bat.jinja b/script/win_build.bat.jinja deleted file mode 100644 index f8214e1..0000000 --- a/script/win_build.bat.jinja +++ /dev/null @@ -1,85 +0,0 @@ -@ECHO OFF -:: Navigate to project root directory -CD /d {{ repo_root_dir }} -:: Create build directory and enter it -MKDIR bin -CD bin -MKDIR cpp{{ cpp_version }} -CD cpp{{ cpp_version }} - -:: Create internal build directory -MKDIR Win32 -MKDIR x64 -MKDIR documentation -:: Create internal install directory -MKDIR install -CD install -MKDIR Win32_Debug -MKDIR Win32_Release -MKDIR x64_Debug -MKDIR x64_Release -CD .. -:: Create internal MSVC specific install directory -MKDIR msvc_install -CD msvc_install -MKDIR bin -MKDIR include -MKDIR lib -MKDIR share -CD bin -MKDIR Win32 -MKDIR x64 -CD .. -CD lib -MKDIR Win32\Debug -MKDIR Win32\Release -MKDIR x64\Debug -MKDIR x64\Release -CD .. -CD .. - -:: Build for Win32 -CD Win32 -cmake -A Win32 -DCMAKE_CXX_STANDARD={{ cpp_version }} -DYYCC_BUILD_TESTBENCH=ON ../../.. -cmake --build . --config Debug -cmake --install . --prefix=../install/Win32_Debug --config Debug -cmake --build . --config RelWithDebInfo -cmake --install . --prefix=../install/Win32_Release --config RelWithDebInfo -CD .. -:: Build for x64 -CD x64 -cmake -A x64 -DCMAKE_CXX_STANDARD={{ cpp_version }} -DYYCC_BUILD_TESTBENCH=ON ../../.. -cmake --build . --config Debug -cmake --install . --prefix=../install/x64_Debug --config Debug -cmake --build . --config RelWithDebInfo -cmake --install . --prefix=../install/x64_Release --config RelWithDebInfo -CD .. - -{% if build_doc %} -:: Build for documentation -CD documentation -cmake -A x64 -DCMAKE_CXX_STANDARD={{ cpp_version }} -DYYCC_BUILD_DOC=ON ../../.. -cmake --build . --config RelWithDebInfo -cmake --build . --target YYCCDocumentation -cmake --install . --prefix=../install/x64_Release --config RelWithDebInfo -CD .. -{% endif %} - -:: Copy header files -XCOPY install\x64_Release\include msvc_install\include\ /E /Y -:: Copy binary files -COPY install\Win32_Release\bin\YYCCTestbench.exe msvc_install\bin\Win32\YYCCTestbench.exe /Y -COPY install\x64_Release\bin\YYCCTestbench.exe msvc_install\bin\x64\YYCCTestbench.exe /Y -:: Copy library files -COPY install\Win32_Debug\lib\YYCCommonplace.lib msvc_install\lib\Win32\Debug\YYCCommonplace.lib /Y -COPY install\Win32_Release\lib\YYCCommonplace.lib msvc_install\lib\Win32\Release\YYCCommonplace.lib /Y -COPY install\x64_Debug\lib\YYCCommonplace.lib msvc_install\lib\x64\Debug\YYCCommonplace.lib /Y -COPY install\x64_Release\lib\YYCCommonplace.lib msvc_install\lib\x64\Release\YYCCommonplace.lib /Y -{% if build_doc %} -:: Copy documentation files -XCOPY install\x64_Release\share msvc_install\share\ /E /Y -{% endif %} - -:: Leave build directory and report -CD ..\.. -ECHO Windows CMake Build Done