Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add scripts to check C++ examples in documentation #22

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .github/workflows/documentation.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,20 @@ jobs:
UPA_DOCS_VERSION: ${{ github.ref_name }}
steps:
- uses: actions/checkout@v4

# Check C++ examples in documentation
- name: Extract and try to build C++ examples from docs
run: |
tools/extract-cpp.py examples/extracted-cpp README.md
cmake -S . -B build -DCMAKE_CXX_COMPILER=g++ -DCMAKE_CXX_STANDARD=20 -DURL_BUILD_TESTS=OFF -DURL_BUILD_EXTRACTED=ON
cmake --build build
# Run doxygen
- name: Download theme
run: doc/download-theme.sh
- uses: mattnotmitt/doxygen-action@edge

# Deploy docs
- name: Is there a gh-pages branch?
if: env.upa_deploy
run: echo "upa_checkout=$(git ls-remote --heads https://github.com/$upa_docs_repository.git refs/heads/gh-pages)" >> "$GITHUB_ENV"
Expand Down
14 changes: 10 additions & 4 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,10 @@ if (NOT DEFINED URL_MAIN_PROJECT)
endif()

# Options
option(URL_BUILD_TESTS "Build the URL tests." ${URL_MAIN_PROJECT})
option(URL_BUILD_FUZZER "Build the URL fuzzer." OFF)
option(URL_BUILD_EXAMPLES "Build the URL examples." OFF)
option(URL_BUILD_TESTS "Build the Upa URL tests." ${URL_MAIN_PROJECT})
option(URL_BUILD_FUZZER "Build the Upa URL fuzzer." OFF)
option(URL_BUILD_EXAMPLES "Build the Upa URL examples." OFF)
option(URL_BUILD_EXTRACTED "Build Upa URL examples extracted from the docs." OFF)
option(URL_BUILD_TOOLS "Build tools." OFF)
option(URL_INSTALL "Generate the install target." ON)
# library options
Expand Down Expand Up @@ -99,7 +100,8 @@ endif()
include_directories(deps)

# Are Upa URL and ICU libraries needed?
if (URL_BUILD_TESTS OR URL_BUILD_FUZZER OR URL_BUILD_EXAMPLES OR URL_INSTALL OR NOT URL_BUILD_TOOLS)
if (URL_BUILD_TESTS OR URL_BUILD_FUZZER OR URL_BUILD_EXAMPLES OR URL_BUILD_EXTRACTED OR
URL_INSTALL OR NOT URL_BUILD_TOOLS)
# This library depends on ICU
find_package(ICU REQUIRED COMPONENTS i18n uc)

Expand Down Expand Up @@ -199,6 +201,10 @@ if (URL_BUILD_EXAMPLES)
target_link_libraries(urlparse ${upa_lib_target})
endif()

if (URL_BUILD_EXTRACTED)
add_subdirectory(examples/extracted-cpp)
endif()

# Tool's targets

if (URL_BUILD_TOOLS)
Expand Down
1 change: 1 addition & 0 deletions examples/extracted-cpp/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
*.cpp
8 changes: 8 additions & 0 deletions examples/extracted-cpp/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Examples to build
file(GLOB example_sources *.cpp)

foreach(source ${example_sources})
get_filename_component(exe_name ${source} NAME_WE)
add_executable(${exe_name} ${source})
target_link_libraries(${exe_name} upa::url)
endforeach()
100 changes: 100 additions & 0 deletions tools/extract-cpp.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
#!/usr/bin/env python3
#
# Copyright 2023 Rimas Misevičius
# Distributed under the BSD-style license that can be
# found in the LICENSE file.
from enum import auto, Enum
import os
import sys

INDENT_TEXT = " "
COMMON_CPP_HEADER = """
#include \"upa/url.h\"
#include <iostream>
#include <string>
"""

def has_main(cpp_text):
return "int main(" in cpp_text

class CppExamples:
class State(Enum):
Outside = auto()
EnterCpp = auto()
InCpp = auto()
InCppSnipet = auto()

def __init__(self, out_path):
self._cpp_file_num = 0
self._cpp_example_num = 0
self._cpp_examples_text = COMMON_CPP_HEADER
self._out_path = out_path

def extract_cpp_from_md_file(self, md_path):
state = self.State.Outside.value
cpp_text = ""

# Open the MD file
print("Processing Markdown file:", md_path)
with open(md_path, "r") as file:
for line_nl in file:
# remove ending newline character
line = line_nl.rstrip()
if state == self.State.Outside.value:
if line == "```cpp":
state = self.State.EnterCpp.value;
cpp_text = ""
elif line == "```":
if state == self.State.InCpp.value:
if has_main(cpp_text):
self._cpp_file_num += 1
out_file_path = os.path.join(self._out_path, f"example-{self._cpp_file_num}.cpp")
print(" creating:", out_file_path)
with open(out_file_path, "w") as out_file:
out_file.write(f"// Example from: {md_path}\n")
out_file.write(cpp_text)
elif state == self.State.InCppSnipet.value:
self._cpp_example_num += 1
self._cpp_examples_text += f"\n// Example from: {md_path}\n"
self._cpp_examples_text += f"void example_{self._cpp_example_num}() {{\n"
self._cpp_examples_text += cpp_text
self._cpp_examples_text += "}\n"
state = self.State.Outside.value;
else:
if state == self.State.EnterCpp.value:
state = self.State.InCpp.value if line.startswith("#include") else self.State.InCppSnipet.value
if state == self.State.InCppSnipet.value:
cpp_text += INDENT_TEXT
cpp_text += line_nl

def output_common_cpp(self):
if self._cpp_example_num > 0:
# create main()
self._cpp_examples_text += "\nint main() {\n"
for num in range(1, self._cpp_example_num + 1):
self._cpp_examples_text += f"{INDENT_TEXT}example_{num}();\n"
self._cpp_examples_text += f"{INDENT_TEXT}return 0;\n"
self._cpp_examples_text += "}\n"
# save code
out_file_path = os.path.join(self._out_path, "examples.cpp")
print(" creating:", out_file_path)
with open(out_file_path, "w") as out_file:
out_file.write(self._cpp_examples_text)


if __name__ == "__main__":
# Command line arguments
if len(sys.argv) >= 3:
out_path = sys.argv[1]
md_files = sys.argv[2:]

print("Output directory:", out_path)
cpp_examples = CppExamples(out_path)
for md_path in md_files:
cpp_examples.extract_cpp_from_md_file(md_path)
cpp_examples.output_common_cpp()
else:
app_name = os.path.basename(os.path.basename(__file__))
print(f"Usage: {app_name} <output dir> <markdown file> ...",
file=sys.stderr)
sys.exit(1)
Loading