diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 541223b5..ca119789 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -36,7 +36,26 @@ jobs: uses: ./complexity with: path: coreMQTT - test-doxygen-check: + test-doxygen-zip-check: + runs-on: ubuntu-20.04 + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-python@v2 + with: + python-version: '3.7.10' + - uses: actions/checkout@v2 + with: + repository: aws/aws-iot-device-sdk-embedded-C + submodules: recursive + ref: main + path: aws-iot-device-sdk-embedded-C + - name: Test doxygen build action + uses: ./doxygen + with: + path: ./aws-iot-device-sdk-embedded-C + libs_parent_dir_path: libraries/standard,libraries/aws + generate_zip: true + test-doxygen-non-zip-check: runs-on: ubuntu-20.04 steps: - uses: actions/checkout@v2 diff --git a/doxygen/action.yml b/doxygen/action.yml index 296bd5cf..555da863 100644 --- a/doxygen/action.yml +++ b/doxygen/action.yml @@ -5,10 +5,19 @@ inputs: description: 'Path to repository folder to run doxygen generation in.' required: false default: ./ + libs_parent_dir_path: + description: 'Comma-separated list of parent directory path of libraries. Path should be relative to repository root.' + required: false + default: ./ doxygen_link: description: 'Download link for doxygen tar.gz (default version 1.8.20).' required: false default: "https://sourceforge.net/projects/doxygen/files/rel-1.8.20/doxygen-1.8.20.linux.bin.tar.gz" + generate_zip: + description: 'Flag to indicate whether a ZIP output should be created.' + required: false + default: 'false' + runs: using: "composite" steps: @@ -17,9 +26,13 @@ runs: wget -qO- "${{ inputs.doxygen_link }}" | sudo tar --strip-components=1 -xz -C /usr/local sudo apt-get install -y libclang-9-dev graphviz shell: bash - - name: Run Doxygen And Verify Stdout Is Empty + - name: Verify Doxygen build and Generate ZIP (if specified) working-directory: ${{ inputs.path }} run: | - doxygen docs/doxygen/config.doxyfile 2>&1 | tee doxyoutput.txt - if [[ "$(wc -c < doxyoutput.txt | bc)" = "0" ]]; then exit 0; else exit 1; fi + if [[ ${{ inputs.generate_zip }} == "true" ]]; then + python3 $GITHUB_ACTION_PATH/generate_doxygen.py --root ./ --library-directories ${{ inputs.libs_parent_dir_path }} --zip + else + doxygen docs/doxygen/config.doxyfile 2>&1 | tee doxyoutput.txt + if [[ "$(wc -c < doxyoutput.txt | bc)" = "0" ]]; then exit 0; else exit 1; fi + fi shell: bash diff --git a/doxygen/generate_doxygen.py b/doxygen/generate_doxygen.py new file mode 100644 index 00000000..f2d9c8e1 --- /dev/null +++ b/doxygen/generate_doxygen.py @@ -0,0 +1,107 @@ +#!/usr/bin/env python3 +import subprocess +import sys +import argparse +import os +import zipfile + +# Executes the passed command in a shell subprocess. +# Returns True if any output is received from the shell process. +def run_cmd(cmd): + """ + Execute the input command on the shell. + """ + print(f"Executing command: {cmd}") + try: + result = subprocess.run( + cmd, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + shell=True, + encoding="utf-8", + check=True, + timeout=180, + ) + print( result.stdout ) + return (len(result.stdout) > 0) + except subprocess.CalledProcessError as e: + result = e.stdout + return result + +def get_lib_paths(root, lib_parent_dirs): + """ + Get all of the paths, relative to the root, to the libraries for which doxygen + output has to be generated. + """ + abs_lib_paths = [] + + for lib_parent_dir in lib_parent_dirs: + abs_lib_path_dir = os.path.join(root, lib_parent_dir) + lib_path_dirs = os.listdir(abs_lib_path_dir) + # Determine if it is a library path by checking if the directory contains "docs/doxygen" + abs_lib_paths += [os.path.join(abs_lib_path_dir, dir) for dir in lib_path_dirs if os.path.exists(os.path.join(abs_lib_path_dir, dir, "docs", "doxygen"))] + + return abs_lib_paths + + +def main(): + """ + Generate documentation and optionally zip it up. + """ + parser = argparse.ArgumentParser(description="Generate all doxygen and optionally zip it.") + parser.add_argument("-r", "--root", action="store", required=False, dest="root", help="Repository root path. This defaults to the current working directory.") + parser.add_argument("-d", "--library-directories", action="store", required=True, dest="lib_dirs", help="Comma-separate list of parent directories of libraries. These should be relative to repository root.") + parser.add_argument("-z", "--zip", action="store_true", required=False, help="Zip all doxygen output.") + args = parser.parse_args() + repo_root = args.root + doZip = args.zip + + # If the root folder is not give, use the current working directory. + if( repo_root == None ): + repo_root = os.getcwd() + + # Get the absolute paths to all of the libraries in the repository. + abs_repo_root = os.path.abspath(repo_root) + abs_lib_paths = get_lib_paths(abs_repo_root, args.lib_dirs.split(',')) + abs_doxy_paths = [] + doxygen_warnings_flag = False + + # Generate the doxygen for all of the libraries. + for abs_lib_path in abs_lib_paths: + abs_doxy_path = os.path.join(abs_lib_path, "docs", "doxygen") + os.chdir(abs_lib_path) + print(abs_lib_path) + # If there is something printed by doxygen, then it represents a warning. + doxygen_warnings_flag = run_cmd("doxygen docs/doxygen/config.doxyfile") or doxygen_warnings_flag + abs_doxy_paths.append(abs_doxy_path) + + # Generate the doxygen for the repository to use the tags from the libraries. + if os.path.exists(os.path.join(abs_repo_root,"docs","doxygen")): + os.chdir(abs_repo_root) + print(abs_repo_root) + doxygen_warnings_flag = run_cmd("doxygen docs/doxygen/config.doxyfile") or doxygen_warnings_flag + abs_doxy_paths.append(os.path.join(abs_repo_root, "docs", "doxygen")) + + # Zip up if desired. + if not(doxygen_warnings_flag) and doZip: + os.chdir(abs_repo_root) + print(f"Zipping up to {abs_repo_root}{os.path.sep}doxygen.zip...") + doxy_zip = zipfile.ZipFile("doxygen.zip", mode="w") + try: + for abs_doxy_path in abs_doxy_paths: + abs_output_dir = os.path.join(abs_doxy_path, "output") + for out_root, _, out_files in os.walk(abs_output_dir): + rel_out_root = os.path.relpath(out_root, abs_repo_root) + for out_file in out_files: + doxy_zip.write(os.path.join(rel_out_root, out_file)) + finally: + doxy_zip.close() + + # Return failure exit code if doxygen generation resulted in warnings. + if doxygen_warnings_flag == False: + sys.exit(0) + else: + sys.exit(1) + +if __name__ == "__main__": + main()