diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100755 index 0000000..8764130 --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,85 @@ +FROM debian:bullseye-slim as devroot + +LABEL MAINTAINER="Barre Kevin" +LABEL VERSION="1.0" +LABEL SRC=https://github.com/neudinger/ \ + DESCRIPTION="PDIC Container" + + +ARG USERNAME=pdi +ARG USER_UID=1000 +ENV PATH /bin:/sbin:/usr/bin:$PATH +ENV DEBIAN_FRONTEND=noninteractive + +ENV LOGGING_LEVEL=INFO +ENV LANG=C.UTF-8 LC_ALL=C.UTF-8 HOME=/home/${USERNAME} +ENV PATH=/usr/sbin:/bin:/sbin:/usr/bin:${HOME}/.local/bin:/usr/local/bin +ENV USER_NAME=$USERNAME +ENV CONDA_DIR=/opt/conda +ENV PATH=${CONDA_DIR}/bin:${PATH} + +RUN set -eux && \ + apt update -y && apt install -y --no-install-recommends \ + curl ca-certificates tar build-essential sudo git binutils-gold \ + libtool libtool-bin zlib1g-dev libssl-dev \ + libncursesw5 libpthread-stubs0-dev \ + clang-format clang-tidy clang-tools clang clangd libc++-dev \ + libc++1 libc++abi-dev libc++abi1 libclang-dev libclang1 liblldb-dev \ + libllvm-ocaml-dev libomp-dev libomp5 lld lldb llvm-dev llvm-runtime llvm python-clang +RUN update-alternatives --install "/usr/bin/ld" "ld" `which ld.gold` 10 + +SHELL ["/bin/bash", "-c"] + +USER root + +WORKDIR $HOME + +RUN groupadd --gid $USER_UID $USERNAME; \ + useradd -s /bin/bash --uid $USER_UID --gid $USER_UID -m $USERNAME + +RUN echo $USERNAME ALL=\(root\) NOPASSWD:ALL > /etc/sudoers.d/$USERNAME +RUN chmod 0440 /etc/sudoers.d/$USERNAME +RUN apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false + +WORKDIR ${HOME} + +ENV APP_HOME ${HOME}/app/ +RUN set -eux;\ + mkdir -p ${APP_HOME} && \ + chown -R ${USERNAME}:${USERNAME} ${HOME} + +SHELL ["/bin/bash", "-c"] + +WORKDIR $HOME +# MiniConda + +RUN set -eux && \ + curl --progress-bar -L \ + https://github.com/conda-forge/miniforge/releases/latest/download/Miniforge3-$(uname)-$(uname -m).sh -o Miniforge3.sh && \ + bash Miniforge3.sh -b -p ${CONDA_DIR} && \ + rm Miniforge3.sh + +RUN set -eux && \ + curl --progress-bar -L \ + https://github.com/conda-forge/miniforge/releases/latest/download/Mambaforge-pypy3-$(uname)-$(uname -m).sh -o Mambaforge.sh && \ + bash Mambaforge.sh -b -u ${CONDA_DIR} && \ + rm Mambaforge.sh + +COPY requirement.yml ${HOME} + +RUN set -eux; \ + conda env create --name pdic-env -f requirement.yml && \ + conda init bash && \ + rm requirement.yml + +ENV PATH=$HOME/miniconda/envs/pdic-env/bin:${PATH} +RUN echo "PATH=${PATH}" >> ~/.bashrc +WORKDIR $APP_HOME +SHELL ["/bin/bash", "-c"] +RUN echo "conda activate pdic-env" >> ~/.bashrc +COPY --chown=${USER_NAME}:${USER_NAME} entrypoint.sh ${HOME} +RUN chown -R ${USER_NAME}:${USER_NAME} /home +ENTRYPOINT ["/bin/bash", "/home/pdi/entrypoint.sh"] +CMD ["bash"] +# USER root +USER $USERNAME \ No newline at end of file diff --git a/.devcontainer/conda-github-requirement.yml b/.devcontainer/conda-github-requirement.yml new file mode 100644 index 0000000..a01586d --- /dev/null +++ b/.devcontainer/conda-github-requirement.yml @@ -0,0 +1,11 @@ +channels: + - defaults + - conda-forge +dependencies: + - python=3.9 + - pip + - twine + - mkdocs-material + - ninja + - pip: + - "pdoc3" diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100755 index 0000000..4fce5fa --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,9 @@ +{ + "name": "PDIC container dev", + "dockerFile": "Dockerfile", + "extensions": [ + "ms-python.python", + "austin.code-gnu-global", + "ms-vscode.cmake-tools" + ] +} \ No newline at end of file diff --git a/.devcontainer/dockross.dockerfile b/.devcontainer/dockross.dockerfile new file mode 100644 index 0000000..a95722e --- /dev/null +++ b/.devcontainer/dockross.dockerfile @@ -0,0 +1,14 @@ +# https://github.com/dockcross/dockcross + +FROM dockcross/base + +ENV DEFAULT_DOCKCROSS_IMAGE pdic_cross_build +RUN set -eux && \ + apt update -y && apt install -y --no-install-recommends \ + curl ca-certificates tar build-essential sudo git binutils-gold \ + libtool libtool-bin zlib1g-dev libssl-dev \ + libncursesw5 libpthread-stubs0-dev \ + clang-format clang-tidy clang-tools clang clangd libc++-dev \ + libc++1 libc++abi-dev libc++abi1 libclang-dev libclang1 liblldb-dev \ + libllvm-ocaml-dev libomp-dev libomp5 lld lldb llvm-dev llvm-runtime llvm python-clang +RUN pip install pdoc3 twine pyaml diff --git a/.devcontainer/entrypoint.sh b/.devcontainer/entrypoint.sh new file mode 100755 index 0000000..f63f267 --- /dev/null +++ b/.devcontainer/entrypoint.sh @@ -0,0 +1,4 @@ +# !/bin/bash --login +# activate conda environment and let the following process take over +source ~/.bashrc +exec "$@" \ No newline at end of file diff --git a/.devcontainer/requirement.yml b/.devcontainer/requirement.yml new file mode 100644 index 0000000..377123e --- /dev/null +++ b/.devcontainer/requirement.yml @@ -0,0 +1,14 @@ +channels: + - defaults + - conda-forge +dependencies: + - python=3.9 + - pip + - cmake=3.21.2 + - scikit-build + - ninja + - twine + - pytest + - pyyaml + - pip: + - "pdoc3" diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..8f3bf3b --- /dev/null +++ b/.dockerignore @@ -0,0 +1,11 @@ +llvm-project-llvmorg-12.0.1/ +build/ +docs/_build +tests/bin +Testing/ +CMakeFiles/ +dist/ +html/ +.pytest_cache/ +_skbuild/ +.vscode diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml new file mode 100644 index 0000000..ee25232 --- /dev/null +++ b/.github/workflows/ci-cd.yml @@ -0,0 +1,129 @@ +name: Build pdic +on: + push: + tags: + - "*" +jobs: + create_release: + if: contains(github.ref, 'v') + name: Create release + runs-on: ubuntu-latest + outputs: + upload_url: ${{ steps.create_release.outputs.upload_url }} + id: ${{ steps.create_release.outputs.id }} + steps: + - name: Create release + id: create_release + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: ${{ github.ref }} + release_name: Release ${{ github.ref }} + draft: false + prerelease: false + build: + name: Build ${{ matrix.os }} + needs: create_release + runs-on: ${{ matrix.os }} + strategy: + fail-fast: true + matrix: + os: [ubuntu-20.04] # , macOS-10.15, windows-2019 + if: contains(github.ref, 'v') + steps: + - name: Checkout code + uses: actions/checkout@v2 + - name: Get the version + id: get_version + run: | + echo ::set-output name=VERSION::$(echo $GITHUB_REF | cut -d / -f 3) + - name: Install system requirement + if: ${{ matrix.os == 'ubuntu-20.04' }} + run: | + sudo apt update && sudo apt install -y \ + ca-certificates sudo git + id: checkout + - name: Conda activate + uses: conda-incubator/setup-miniconda@v2 + with: + miniconda-version: "latest" + activate-environment: pdic-env + - name: Install conda dependencies + shell: bash -l {0} + id: installation + run: | + set -eux; conda env update --name pdic-env --file .devcontainer/conda-github-requirement.yml + - name: Deploy mkdocs + if: ${{ contains(github.ref, 'p') && matrix.os != 'windows-2019'}} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + shell: bash -l {0} + run: | + mkdocs gh-deploy --force + - name: Login to DockerHub + uses: docker/login-action@v1 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + - name: Build Docker image + run: docker build . -f .devcontainer/Dockerfile -t pdic:latest + - name: Build in Docker image + id: builder + shell: bash -l {0} + run: | + version=${{ steps.get_version.outputs.VERSION }} + export version=${version//[!0-9.]/} + echo ::set-output name=PYVERSION::$(echo ${version}) + sudo chmod -R a+w `pwd` + docker run -e version=${version} -v `pwd`:/home/pdi/app pdic python3 setup.py build + docker run -e version=${version} -v `pwd`:/home/pdi/app pdic python3 setup.py bdist_wheel --plat-name manylinux1_x86_64 -j `nproc` + docker run -e version=${version} -v `pwd`:/home/pdi/app pdic python3 setup.py bdist_wheel --plat-name manylinux2010_x86_64 -j `nproc` + docker run -e version=${version} -v `pwd`:/home/pdi/app pdic python3 setup.py build_ext --inplace -j `nproc` + docker run -v `pwd`:/home/pdi/app pdic cmake -S . -DBINARY:BOOL=ON -B build + docker run -v `pwd`:/home/pdi/app pdic cmake --build build -- -j `nproc` + docker run -v `pwd`:/home/pdi/app pdic cmake -S . -DTEST:BOOL=ON -B build + docker run -v `pwd`:/home/pdi/app pdic cmake --build build -- -j `nproc` + docker run -v `pwd`:/home/pdi/app pdic /bin/bash -c "pip install dist/pdic-${version}-cp39-cp39-manylinux1_x86_64.whl && pytest" + docker run -v `pwd`:/home/pdi/app pdic ctest --test-dir build + docker run -v `pwd`:/home/pdi/app pdic /bin/bash -c "pip install dist/pdic-${version}-cp39-cp39-manylinux1_x86_64.whl && pdoc3 pdic --pdf > dist/pdic-pdoc3-${version}.md && pdoc3 pdic --html -o dist/" + sudo chown -R $USER `pwd` + mv build/pdic.static dist/ + mv ./docs_format/*.ttf . + - name: Build pandoc documentation + uses: docker://pandoc/latex + with: + args: >- # allows you to break string into multiple lines + --metadata=title:"PDIC: (IDL) Transpiler Source-to-source compiler C/C++ to YAML Description based on clang " + --variable=documentclass:"extarticle" + --variable=mainfont:"Roboto" + --variable=mainfontoptions:"Extension=.ttf, UprightFont=*-Regular, BoldFont=*-Bold, ItalicFont=*-Italic, BoldItalicFont=*-BoldItalic" + --variable=fontsize:"10pt" + --variable=geometry:"margin=1.5cm,a4paper" + --metadata=author:"BARRE kevin" + --from=markdown+abbreviations+tex_math_single_backslash + --pdf-engine=xelatex + --toc --toc-depth=4 + --output=dist/pdic-pdoc3-${{ steps.builder.outputs.PYVERSION }}.pdf + dist/pdic-pdoc3-${{ steps.builder.outputs.PYVERSION }}.md + - name: Upload Releases Assets + id: upload-release-assets + uses: dwenegar/upload-release-assets@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + release_id: ${{ needs.create_release.outputs.id }} + assets_path: ./dist/ + - name: Deploy to PyPI ${{ matrix.os }} + if: ${{ contains(github.ref, 'p') && matrix.os != 'windows-2019'}} + shell: bash -l {0} + env: + TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }} + run: | + twine upload -u __token__ -p $TWINE_PASSWORD dist/*.whl + - name: Deploy to PyPI windows-2019 + if: ${{ contains(github.ref, 'p') && matrix.os == 'windows-2019'}} + run: | + pip install twine + twine upload -u __token__ -p ${{ secrets.PYPI_PASSWORD }} dist/*.whl diff --git a/.gitignore b/.gitignore index 32c0064..49bd1a3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,76 @@ -llvm-project-llvmorg-12.0.1/ -dockerfiles/ +# +# File: .gitignore +# Project: pdic +# File Created: Thursday, 22nd July 2021 11:05:12 pm +# Author: kbarre (kevin.barre@epitech.eu) +# ----- +# Last Modified: Friday, 30th July 2021 6:04:17 pm +# Modified By: kbarre (kevin.barre@epitech.eu>) +# ----- +# Licenses: EUPL +# ----- +# Copyright 2021 - 2021 neudinger +# + +# Build directory build/ +llvm-project-llvmorg-12.0.1/ +docs/_build +tests/bin +Testing/ +CMakeFiles/ +dist/ +html/ +.pytest_cache/ +_skbuild/ + +# Python +*.egg-info +__pycache__ +.eggs +*.pyc + +# VS Code +.vscode + +# Prerequisites *.yml *.tar* -documentation/ -!examples/expected/*.yml \ No newline at end of file +!_config.yml +!mkdocs.yml +!.github/workflows/ci-cd.yml +!examples/expected/*.yml +!.devcontainer/*.yml +!requirement.yml +__version__.py + +# Compiled Object files +*.slo +*.lo +*.o +*.obj +CMakeCache* +Makefile + +# Precompiled Headers +*.gch +*.pch + +# Compiled python files +core + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app diff --git a/CMakeLists.txt b/CMakeLists.txt index 7015ff4..759337c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,51 +1,58 @@ -cmake_minimum_required(VERSION 3.20) -project(pdi) +cmake_minimum_required(VERSION 3.21) set(TARGET "pdic") +project(${TARGET} VERSION 0.1) +include(FetchContent) -find_package(LLVM REQUIRED CONFIG) -find_package(Clang REQUIRED CONFIG) +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) -message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}") -message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}") +option(DEBUG "DEBUG option" OFF) # OFF by default +message(STATUS "DEBUG is " ${DEBUG}) +option(SKBUILD "SKBUILD option" OFF) # OFF by default +message(STATUS "SKBUILD is " ${SKBUILD}) +option(BINARY "BINARY option" OFF) # OFF by default +message(STATUS "BINARY is " ${BINARY}) +option(TEST "TEST option" OFF) # OFF by default +message(STATUS "TEST is " ${TEST}) -# Use given `clang` toolchain. -set(CMAKE_CXX_COMPILER "${LLVM_TOOLS_BINARY_DIR}/clang++") -set(CMAKE_C_COMPILER "${LLVM_TOOLS_BINARY_DIR}/clang") -list(APPEND CMAKE_MODULE_PATH "${LLVM_CMAKE_DIR}") -# set(CMAKE_CXX_STANDARD 20) -# set(CMAKE_CXX_STANDARD_REQUIRED ON) -# set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++2a") +if(DEBUG) + add_compile_definitions(DEBUG=True) +endif(DEBUG) -# will remove `--` compilation command -# and add compilation databases for clang on this project. -# set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DCMAKE_EXPORT_COMPILE_COMMANDS") +set(WORKDIR ${CMAKE_CURRENT_LIST_DIR}/src/pdic) +message(STATUS "WORKDIR in: ${WORKDIR}") -# Set compiler flags. -include(HandleLLVMOptions) -# Use LLVM and clang headers. -include_directories(${LLVM_INCLUDE_DIRS} ${CLANG_INCLUDE_DIRS}) - -include_directories(${LLVM_INCLUDE_DIRS}) -add_definitions(${LLVM_DEFINITIONS}) -list(APPEND CMAKE_MODULE_PATH "${LLVM_CMAKE_DIR}") -include(AddLLVM) -include("./llvm-project-llvmorg-12.0.1/clang/cmake/modules/AddClang.cmake") - -file(GLOB src - "src/*.hpp" - "src/*.cpp" +file(GLOB SRC + "${WORKDIR}/*.hpp" + "${WORKDIR}/*.cpp" ) -add_clang_executable(${TARGET} ${src}) - -target_link_libraries(${TARGET} - PRIVATE - clangAST - clangBasic - clangFrontend - clangTooling - clangASTMatchers +list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/modules) + + +if((BINARY OR TEST) OR SKBUILD) + include(Builder) +else((BINARY OR TEST) OR SKBUILD) + message(" + Please Use one at least one cmake flag \n\ + But only one between\n\ + - SKBUILD : python lib\n\ + - BINARY : embedded binary\n\ + - TEST : ctest\n\n\ + + cmake -S . \n\ + -DSKBUILD:BOOL=OFF # or ON\n\ + -DBINARY:BOOL=ON # or OFF\n\ + -DTEST:BOOL=OFF # or ON\n\ + -DDEBUG:BOOL=ON # or OFF\n\ + -B build " ) +endif((BINARY OR TEST) OR SKBUILD) + +set(ignoreMe "${SKBUILD}") -# cmake .. ; make && ./pdic ../../PDI_test_parse.h -- \ No newline at end of file +unset(DEBUG CACHE) +unset(SKBUILD CACHE) +unset(BINARY CACHE) +unset(TEST CACHE) \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..46d8a57 --- /dev/null +++ b/LICENSE @@ -0,0 +1,190 @@ +EUROPEAN UNION PUBLIC LICENCE v. 1.2 +EUPL © the European Union 2007, 2016 + +This European Union Public Licence (the ‘EUPL’) applies to the Work (as defined below) which is provided under the +terms of this Licence. Any use of the Work, other than as authorised under this Licence is prohibited (to the extent such +use is covered by a right of the copyright holder of the Work). +The Work is provided under the terms of this Licence when the Licensor (as defined below) has placed the following +notice immediately following the copyright notice for the Work: + Licensed under the EUPL +or has expressed by any other means his willingness to license under the EUPL. + +1.Definitions +In this Licence, the following terms have the following meaning: +— ‘The Licence’:this Licence. +— ‘The Original Work’:the work or software distributed or communicated by the Licensor under this Licence, available +as Source Code and also as Executable Code as the case may be. +— ‘Derivative Works’:the works or software that could be created by the Licensee, based upon the Original Work or +modifications thereof. This Licence does not define the extent of modification or dependence on the Original Work +required in order to classify a work as a Derivative Work; this extent is determined by copyright law applicable in +the country mentioned in Article 15. +— ‘The Work’:the Original Work or its Derivative Works. +— ‘The Source Code’:the human-readable form of the Work which is the most convenient for people to study and +modify. +— ‘The Executable Code’:any code which has generally been compiled and which is meant to be interpreted by +a computer as a program. +— ‘The Licensor’:the natural or legal person that distributes or communicates the Work under the Licence. +— ‘Contributor(s)’:any natural or legal person who modifies the Work under the Licence, or otherwise contributes to +the creation of a Derivative Work. +— ‘The Licensee’ or ‘You’:any natural or legal person who makes any usage of the Work under the terms of the +Licence. +— ‘Distribution’ or ‘Communication’:any act of selling, giving, lending, renting, distributing, communicating, +transmitting, or otherwise making available, online or offline, copies of the Work or providing access to its essential +functionalities at the disposal of any other natural or legal person. + +2.Scope of the rights granted by the Licence +The Licensor hereby grants You a worldwide, royalty-free, non-exclusive, sublicensable licence to do the following, for +the duration of copyright vested in the Original Work: +— use the Work in any circumstance and for all usage, +— reproduce the Work, +— modify the Work, and make Derivative Works based upon the Work, +— communicate to the public, including the right to make available or display the Work or copies thereof to the public +and perform publicly, as the case may be, the Work, +— distribute the Work or copies thereof, +— lend and rent the Work or copies thereof, +— sublicense rights in the Work or copies thereof. +Those rights can be exercised on any media, supports and formats, whether now known or later invented, as far as the +applicable law permits so. +In the countries where moral rights apply, the Licensor waives his right to exercise his moral right to the extent allowed +by law in order to make effective the licence of the economic rights here above listed. +The Licensor grants to the Licensee royalty-free, non-exclusive usage rights to any patents held by the Licensor, to the +extent necessary to make use of the rights granted on the Work under this Licence. + +3.Communication of the Source Code +The Licensor may provide the Work either in its Source Code form, or as Executable Code. If the Work is provided as +Executable Code, the Licensor provides in addition a machine-readable copy of the Source Code of the Work along with +each copy of the Work that the Licensor distributes or indicates, in a notice following the copyright notice attached to +the Work, a repository where the Source Code is easily and freely accessible for as long as the Licensor continues to +distribute or communicate the Work. + +4.Limitations on copyright +Nothing in this Licence is intended to deprive the Licensee of the benefits from any exception or limitation to the +exclusive rights of the rights owners in the Work, of the exhaustion of those rights or of other applicable limitations +thereto. + +5.Obligations of the Licensee +The grant of the rights mentioned above is subject to some restrictions and obligations imposed on the Licensee. Those +obligations are the following: + +Attribution right: The Licensee shall keep intact all copyright, patent or trademarks notices and all notices that refer to +the Licence and to the disclaimer of warranties. The Licensee must include a copy of such notices and a copy of the +Licence with every copy of the Work he/she distributes or communicates. The Licensee must cause any Derivative Work +to carry prominent notices stating that the Work has been modified and the date of modification. + +Copyleft clause: If the Licensee distributes or communicates copies of the Original Works or Derivative Works, this +Distribution or Communication will be done under the terms of this Licence or of a later version of this Licence unless +the Original Work is expressly distributed only under this version of the Licence — for example by communicating +‘EUPL v. 1.2 only’. The Licensee (becoming Licensor) cannot offer or impose any additional terms or conditions on the +Work or Derivative Work that alter or restrict the terms of the Licence. + +Compatibility clause: If the Licensee Distributes or Communicates Derivative Works or copies thereof based upon both +the Work and another work licensed under a Compatible Licence, this Distribution or Communication can be done +under the terms of this Compatible Licence. For the sake of this clause, ‘Compatible Licence’ refers to the licences listed +in the appendix attached to this Licence. Should the Licensee's obligations under the Compatible Licence conflict with +his/her obligations under this Licence, the obligations of the Compatible Licence shall prevail. + +Provision of Source Code: When distributing or communicating copies of the Work, the Licensee will provide +a machine-readable copy of the Source Code or indicate a repository where this Source will be easily and freely available +for as long as the Licensee continues to distribute or communicate the Work. +Legal Protection: This Licence does not grant permission to use the trade names, trademarks, service marks, or names +of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and +reproducing the content of the copyright notice. + +6.Chain of Authorship +The original Licensor warrants that the copyright in the Original Work granted hereunder is owned by him/her or +licensed to him/her and that he/she has the power and authority to grant the Licence. +Each Contributor warrants that the copyright in the modifications he/she brings to the Work are owned by him/her or +licensed to him/her and that he/she has the power and authority to grant the Licence. +Each time You accept the Licence, the original Licensor and subsequent Contributors grant You a licence to their contributions +to the Work, under the terms of this Licence. + +7.Disclaimer of Warranty +The Work is a work in progress, which is continuously improved by numerous Contributors. It is not a finished work +and may therefore contain defects or ‘bugs’ inherent to this type of development. +For the above reason, the Work is provided under the Licence on an ‘as is’ basis and without warranties of any kind +concerning the Work, including without limitation merchantability, fitness for a particular purpose, absence of defects or +errors, accuracy, non-infringement of intellectual property rights other than copyright as stated in Article 6 of this +Licence. +This disclaimer of warranty is an essential part of the Licence and a condition for the grant of any rights to the Work. + +8.Disclaimer of Liability +Except in the cases of wilful misconduct or damages directly caused to natural persons, the Licensor will in no event be +liable for any direct or indirect, material or moral, damages of any kind, arising out of the Licence or of the use of the +Work, including without limitation, damages for loss of goodwill, work stoppage, computer failure or malfunction, loss +of data or any commercial damage, even if the Licensor has been advised of the possibility of such damage. However, +the Licensor will be liable under statutory product liability laws as far such laws apply to the Work. + +9.Additional agreements +While distributing the Work, You may choose to conclude an additional agreement, defining obligations or services +consistent with this Licence. However, if accepting obligations, You may act only on your own behalf and on your sole +responsibility, not on behalf of the original Licensor or any other Contributor, and only if You agree to indemnify, +defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against such Contributor by +the fact You have accepted any warranty or additional liability. + +10.Acceptance of the Licence +The provisions of this Licence can be accepted by clicking on an icon ‘I agree’ placed under the bottom of a window +displaying the text of this Licence or by affirming consent in any other similar way, in accordance with the rules of +applicable law. Clicking on that icon indicates your clear and irrevocable acceptance of this Licence and all of its terms +and conditions. +Similarly, you irrevocably accept this Licence and all of its terms and conditions by exercising any rights granted to You +by Article 2 of this Licence, such as the use of the Work, the creation by You of a Derivative Work or the Distribution +or Communication by You of the Work or copies thereof. + +11.Information to the public +In case of any Distribution or Communication of the Work by means of electronic communication by You (for example, +by offering to download the Work from a remote location) the distribution channel or media (for example, a website) +must at least provide to the public the information requested by the applicable law regarding the Licensor, the Licence +and the way it may be accessible, concluded, stored and reproduced by the Licensee. + +12.Termination of the Licence +The Licence and the rights granted hereunder will terminate automatically upon any breach by the Licensee of the terms +of the Licence. +Such a termination will not terminate the licences of any person who has received the Work from the Licensee under +the Licence, provided such persons remain in full compliance with the Licence. + +13.Miscellaneous +Without prejudice of Article 9 above, the Licence represents the complete agreement between the Parties as to the +Work. +If any provision of the Licence is invalid or unenforceable under applicable law, this will not affect the validity or +enforceability of the Licence as a whole. Such provision will be construed or reformed so as necessary to make it valid +and enforceable. +The European Commission may publish other linguistic versions or new versions of this Licence or updated versions of +the Appendix, so far this is required and reasonable, without reducing the scope of the rights granted by the Licence. +New versions of the Licence will be published with a unique version number. +All linguistic versions of this Licence, approved by the European Commission, have identical value. Parties can take +advantage of the linguistic version of their choice. + +14.Jurisdiction +Without prejudice to specific agreement between parties, +— any litigation resulting from the interpretation of this License, arising between the European Union institutions, +bodies, offices or agencies, as a Licensor, and any Licensee, will be subject to the jurisdiction of the Court of Justice +of the European Union, as laid down in article 272 of the Treaty on the Functioning of the European Union, +— any litigation arising between other parties and resulting from the interpretation of this License, will be subject to +the exclusive jurisdiction of the competent court where the Licensor resides or conducts its primary business. + +15.Applicable Law +Without prejudice to specific agreement between parties, +— this Licence shall be governed by the law of the European Union Member State where the Licensor has his seat, +resides or has his registered office, +— this licence shall be governed by Belgian law if the Licensor has no seat, residence or registered office inside +a European Union Member State. + + + Appendix + +‘Compatible Licences’ according to Article 5 EUPL are: +— GNU General Public License (GPL) v. 2, v. 3 +— GNU Affero General Public License (AGPL) v. 3 +— Open Software License (OSL) v. 2.1, v. 3.0 +— Eclipse Public License (EPL) v. 1.0 +— CeCILL v. 2.0, v. 2.1 +— Mozilla Public Licence (MPL) v. 2 +— GNU Lesser General Public Licence (LGPL) v. 2.1, v. 3 +— Creative Commons Attribution-ShareAlike v. 3.0 Unported (CC BY-SA 3.0) for works other than software +— European Union Public Licence (EUPL) v. 1.1, v. 1.2 +— Québec Free and Open-Source Licence — Reciprocity (LiLiQ-R) or Strong Reciprocity (LiLiQ-R+). + +The European Commission may update this Appendix to later versions of the above licences without producing +a new version of the EUPL, as long as they provide the rights granted in Article 2 of this Licence and protect the +covered Source Code from exclusive appropriation. +All other changes or additions to this Appendix require the production of a new EUPL version. diff --git a/README.md b/README.md index bdf07ed..ff49bad 100644 --- a/README.md +++ b/README.md @@ -1,115 +1,68 @@ -# PDIC +# PDIC [![Open Source Love svg1](https://badges.frapsoft.com/os/v1/open-source.svg?v=103)](https://github.com/ellerbrock/open-source-badges/) PDIC: [(IDL)](https://en.wikipedia.org/wiki/Interface_description_language) Transpiler Source-to-source compiler C/C++ to YAML Portable Data Interface Description -## Abstract +Project related to [PDI](https://gitlab.maisondelasimulation.fr/pdidev/pdi) +[![Linux](https://svgshare.com/i/Zhy.svg)](https://svgshare.com/i/Zhy.svg) -Pronounce: PDI SEE +[![GitHub license](https://img.shields.io/badge/license-EUPL-blue.svg)](https://raw.githubusercontent.com/herotc/hero-rotation/master/LICENSE) [![Build Github Status](https://github.com/neudinger/PDIC/workflows/Build%20pdic/badge.svg)](https://github.com/neudinger/PDIC/actions) -[Why clang ?](https://clang.llvm.org/index.html) +[![PyPI version](https://badge.fury.io/py/pdic.svg)](https://badge.fury.io/py/pdic) +[![PyPI implementation](https://img.shields.io/pypi/implementation/pdic.svg)](https://pypi.python.org/pypi/pdic/) -- Fast compiles and low memory use -- Expressive diagnostics (examples) -- GCC compatibility -- Modular library based architecture -- Support diverse clients (refactoring, static analysis, code generation, etc.) -- A real-world, production quality compiler -- A simple and hackable code base -- A single unified parser for C, Objective C, C++, and Objective C++ -- Conformance with C/C++/ObjC and their variants +[![Doc](https://readthedocs.org/projects/pip/badge/?version=latest)](https://neudinger.github.io/PDIC/) +[![made-with-Markdown](https://img.shields.io/badge/Made%20with-Markdown-1f425f.svg)](http://commonmark.org) -This project can see [PDI Portable Data Interface](https://gitlab.maisondelasimulation.fr/pdidev/pdi) representation in c/c++ +[![GitHub release](https://img.shields.io/github/release/neudinger/PDIC.svg)](https://GitHub.com/neudinger/PDIC/releases/) [![Docker](https://badgen.net/badge/icon/docker?icon=docker&label)](https://https://docker.com/) -## Descriptions +From C/C++ to Yaml Description -[![GitHub license](https://img.shields.io/badge/license-EUPL-blue.svg)](https://raw.githubusercontent.com/herotc/hero-rotation/master/LICENSE) +|Simple binary|Python library| +|:-:|:-:| +|static binary|Python 3| +|`./pdic file.c`|`import pdic; result:str = pdic.files_to_pdi()`| -## Usage - -```sh -./pdic ../PDI_test_parse.h -- -``` - -`PDI_test_parse.yml` will be created - -## Installation - -Dependencies: +## Instalation -- cmake -- ninja -- make -- all clang and llvm tools -- boost - -### Simple +Binary ```sh -wget https://github.com/llvm/llvm-project/archive/refs/tags/llvmorg-12.0.1.tar.gz -tar xvf llvmorg-12.0.1.tar.gz -mkdir build && cd build -cmake .. -make +cmake -S . -DBINARY:BOOL=ON -B build && cmake --build build -- -j `nproc` ``` -### Complete - -Follow: [LibTooling and LibASTMatchers](https://clang.llvm.org/docs/LibASTMatchersTutorial.html) installation tutorial if you want to contribute. - -Use `gold` or `lld` because `ld` use too much ram +Google Test ```sh -wget https://github.com/llvm/llvm-project/archive/refs/tags/llvmorg-12.0.1.tar.gz -tar xvf llvmorg-12.0.1.tar.gz -cd llvm-project-llvmorg-12.0.1 -mkdir build && cd build -cmake -G Ninja ../llvm -DLLVM_ENABLE_PROJECTS="clang;clang-tools-extra" -DCMAKE_BUILD_TYPE=Release -DLLVM_USE_LINKER=gold -ninja -# ninja check # Test LLVM only. -# ninja clang-test # Test Clang only. -ninja install -cd ../.. -mkdir build && cd build -cmake .. -make +cmake -S . -DTEST:BOOL=ON -B build && cmake --build build -- -j `nproc` +cd build && ctest ``` -## Examples - -Files can be: - -- file.c -- file.h -- file.hh / file.hpp -- file.cc / file.cpp - -`--` is for [Compilation databases for Clang-based tools](https://eli.thegreenplace.net/2014/05/21/compilation-databases-for-clang-based-tools) -> Note that anything before the double dash “--” is an input to your LibTooling program, argv in main(), while anything after the double dash is an input to Clang itself (you won’t concern yourself with those). (source `LibTooling Example`) +Python ```sh -./pdic ../examples/level_1.c -- +python setup.py build -G "Unix Makefiles" +python3 setup.py bdist_wheel -G "Unix Makefiles" -j `nproc` +python setup.py build_ext -G "Unix Makefiles" --inplace -j `nproc` +pytest ``` -pdic extract all information from [AST](https://en.wikipedia.org/wiki/Abstract_syntax_tree) and write PDI representation. +## Usage -```c +```c++ #pragma pdi on -int global_int; -char global_char; -void *global_pointer; -float **global_float_pointer_of_pointer; -int array[24]; +typedef struct Var8 +{ +#pragma pdi type : int64 + int my_int; + char char_tab[20]; + char my_char; +} var; -// pointer to array of int of size 42 -#pragma pdi type:int32; size:[42] -int **pointer_of_array; +#pragma pdi size:[42] +int **array_of_pointer_of_array[21]; -double *array_of_pointers[21]; - -#pragma pdi type:uint64 -#pragma pdi size:[10][10][10] -unsigned ****my_cube; +var my_var; #pragma pdi off ``` @@ -117,53 +70,80 @@ unsigned ****my_cube; Will be see and describe as follow ```yml +structs: + Var8: + type: record + name: Var8 + alias: [var] + fieldsize: 3 + buffersize: 28 + packed: false + members: + char_tab: { type: array, subtype: char, size: 20 } + my_char: + offset: 24 + type: char + my_int: + type: int64 data: - array: { type: array, subtype: int, size: 24 } - array_of_pointers: { type: array, subtype: { type: pointer, subtype: double }, size: 21 } - global_char: - type: char - global_float_pointer_of_pointer: { type: pointer, subtype: { type: pointer, subtype: float } } - global_int: - type: int - global_pointer: { type: pointer, subtype: void } - my_cube: { type: pointer, subtype: { type: array, subtype: uint64, size: [10, 10, 10] } } - pointer_of_array: { type: pointer, subtype: { type: array, subtype: int32, size: 42 } } + array_of_pointer_of_array: { type: array, subtype: { type: pointer, subtype: { type: array, subtype: int, size: 42 } }, size: 21 } + my_var: + type: record + name: Var8 + alias: [var] + fieldsize: 3 + buffersize: 28 + packed: false + members: + char_tab: { type: array, subtype: char, size: 20 } + my_char: + offset: 24 + type: char + my_int: + type: int64 ``` -You can find differents examples -> [Here](./examples/) - -## Usefull links - -[Clang API](http://man.hubwiz.com/manual/Clang) - -### Clang / LLVM - -#### Official documentation - -- [Clang Plugins doc](https://clang.llvm.org/docs/ClangPlugins.html) -- [Clang Driver Design](https://clang.llvm.org/docs/DriverInternals.html) -- [RecursiveASTVisitor Tutorial](https://clang.llvm.org/docs/RAVFrontendAction.html) - -#### Other documentation - -- [Basics of AST manipulation](https://freecompilercamp.org/clang-AST-basics/) -- [Comment parser](https://danielbeard.io/2016/04/19/clang-frontend-action-part-1.html) -- [Hacking on Clang is surprisingly easy](https://mort.coffee/home/clang-compiler-hacking/) -- [understanding the clang ast](https://jonasdevlieghere.com/understanding-the-clang-ast/) -- [Clang Tutorial: The AST Matcher](https://xinhuang.github.io/posts/2015-02-08-clang-tutorial-the-ast-matcher.html) -- [Clang Tutorial: Finding Declarations](https://xinhuang.github.io/posts/2014-10-19-clang-tutorial-finding-declarations.html) - -#### Clang Tutorial Part: LibTooling Example - -1. [Part I](https://kevinaboos.wordpress.com/2013/07/23/clang-tutorial-part-i-introduction/) -2. [Part II](https://kevinaboos.wordpress.com/2013/07/23/clang-tutorial-part-ii-libtooling-example/) -3. [Part III](https://kevinaboos.wordpress.com/2013/07/29/clang-tutorial-part-iii-plugin-example/) - -#### Pragma - -- [Pragma token kind identifier](https://repo.hca.bsc.es/gitlab/rferrer/llvm-epi-0.8/-/commit/ea4f7c776194c96f8ece1456bc22102c6cbc9a33) -- [Ex custom pragma 'TritonAssert'](https://github.com/quarkslab/clang/commit/0163f52f70e4781ce99710575bb66943125357b2) - -### Padding and packing in c/c++ +Python Usage + +```python +import os +import pathlib +import yaml +import pdic + +here = pathlib.Path(__file__).parent.resolve() +# pip show -f pdic +if __name__ == "__main__": + pdi_yml_description: str = pdic.files_to_pdi([os.path.join(here, "level_1.c"), + os.path.join(here, "level_2.c")]) + print(pdi_yml_description) + print(yaml.dump(yaml.load(pdi_yml_description))) +``` -- +## Index + +1. [Abstract](/docs/1.0-Abstract.md) +2. [Usage](/docs/2.0-Requirement.md) + 1. [Binary](/docs/2.1-Binary.md) + 2. [Python](/docs/2.2-Python.md) +3. [Developpement](/docs/3.0-Developpement.md) + - Code + - Documentation + - Test + - Deploy +4. [Links](/docs/4.0-Links) + - [Autre Documentation](/docs/5.1-Documentations.md) + - Biblio + - Code example +5. [Extra](/docs/5.0-Extra.md) + - Clang LLVM + - Mind Map + - Graph + - UML +6. Contact / Credits + +Credits + +Barre Kevin [neudinger](https://github.com/) (Software Scientist) + +CEA pdi team diff --git a/_config.yml b/_config.yml new file mode 100644 index 0000000..cc35c1d --- /dev/null +++ b/_config.yml @@ -0,0 +1 @@ +theme: jekyll-theme-modernist \ No newline at end of file diff --git a/dockcross-Makefile b/dockcross-Makefile new file mode 100644 index 0000000..cc7d2d6 --- /dev/null +++ b/dockcross-Makefile @@ -0,0 +1,256 @@ + +# +# Parameters +# + +# Name of the docker executable +DOCKER = docker + +# Docker organization to pull the images from +ORG = dockcross + +# Directory where to generate the dockcross script for each images (e.g bin/dockcross-manylinux2014-x64) +BIN = ./bin + +# These images are built using the "build implicit rule" +STANDARD_IMAGES = android-arm android-arm64 android-x86 android-x86_64 \ + linux-x86 linux-x64 linux-x64-clang linux-arm64 linux-arm64-musl linux-arm64-full \ + linux-armv5 linux-armv5-musl linux-m68k-uclibc linux-s390x linux-x64-tinycc \ + linux-armv6 linux-armv6-lts linux-armv6-musl \ + linux-armv7l-musl linux-armv7 linux-armv7a linux-x86_64-full \ + linux-mips linux-ppc64le linux-riscv64 linux-riscv32 linux-xtensa-uclibc \ + windows-static-x86 windows-static-x64 windows-static-x64-posix windows-armv7 \ + windows-shared-x86 windows-shared-x64 windows-shared-x64-posix windows-arm64 + +# Generated Dockerfiles. +GEN_IMAGES = android-arm android-arm64 \ + linux-x86 linux-x64 linux-x64-clang linux-arm64 linux-arm64-musl linux-arm64-full \ + manylinux2014-x64 manylinux2014-x86 \ + manylinux2014-aarch64 \ + web-wasm linux-mips windows-arm64 windows-armv7 \ + windows-static-x86 windows-static-x64 windows-static-x64-posix \ + windows-shared-x86 windows-shared-x64 windows-shared-x64-posix \ + linux-armv7 linux-armv7a linux-armv7l-musl linux-x86_64-full \ + linux-armv6 linux-armv6-lts linux-armv6-musl \ + linux-armv5 linux-armv5-musl linux-ppc64le linux-s390x \ + linux-riscv64 linux-riscv32 linux-m68k-uclibc linux-x64-tinycc linux-xtensa-uclibc + +GEN_IMAGE_DOCKERFILES = $(addsuffix /Dockerfile,$(GEN_IMAGES)) + +# These images are expected to have explicit rules for *both* build and testing +NON_STANDARD_IMAGES = manylinux2014-x64 manylinux2014-x86 \ + manylinux2014-aarch64 web-wasm + +# Docker composite files +DOCKER_COMPOSITE_SOURCES = common.docker common.debian common.manylinux common.buildroot \ + common.crosstool common.windows common-manylinux.crosstool common.dockcross common.label-and-env +DOCKER_COMPOSITE_FOLDER_PATH = common/ +DOCKER_COMPOSITE_PATH = $(addprefix $(DOCKER_COMPOSITE_FOLDER_PATH),$(DOCKER_COMPOSITE_SOURCES)) + +# This list all available images +IMAGES = $(STANDARD_IMAGES) $(NON_STANDARD_IMAGES) + +# Optional arguments for test runner (test/run.py) associated with "testing implicit rule" +linux-x64-tinycc.test_ARGS = --languages C +windows-static-x86.test_ARGS = --exe-suffix ".exe" +windows-static-x64.test_ARGS = --exe-suffix ".exe" +windows-static-x64-posix.test_ARGS = --exe-suffix ".exe" +windows-shared-x86.test_ARGS = --exe-suffix ".exe" +windows-shared-x64.test_ARGS = --exe-suffix ".exe" +windows-shared-x64-posix.test_ARGS = --exe-suffix ".exe" + +# On CircleCI, do not attempt to delete container +# See https://circleci.com/docs/docker-btrfs-error/ +RM = --rm +ifeq ("$(CIRCLECI)", "true") + RM = +endif + +# Tag images with date and Git short hash in addition to revision +TAG := $(shell date '+%Y%m%d')-$(shell git rev-parse --short HEAD) + +# shellcheck executable +SHELLCHECK := shellcheck + +# Defines the level of verification (error, warning, info...) +SHELLCHECK_SEVERITY_LEVEL := error + +# +# images: This target builds all IMAGES (because it is the first one, it is built by default) +# +images: base $(IMAGES) + +# +# test: This target ensures all IMAGES are built and run the associated tests +# +test: base.test $(addsuffix .test,$(IMAGES)) + +# +# Generic Targets (can specialize later). +# + +$(GEN_IMAGE_DOCKERFILES) Dockerfile: %Dockerfile: %Dockerfile.in $(DOCKER_COMPOSITE_PATH) + sed \ + -e '/common.docker/ r $(DOCKER_COMPOSITE_FOLDER_PATH)common.docker' \ + -e '/common.debian/ r $(DOCKER_COMPOSITE_FOLDER_PATH)common.debian' \ + -e '/common.manylinux/ r $(DOCKER_COMPOSITE_FOLDER_PATH)common.manylinux' \ + -e '/common.crosstool/ r $(DOCKER_COMPOSITE_FOLDER_PATH)common.crosstool' \ + -e '/common.buildroot/ r $(DOCKER_COMPOSITE_FOLDER_PATH)common.buildroot' \ + -e '/common-manylinux.crosstool/ r $(DOCKER_COMPOSITE_FOLDER_PATH)common-manylinux.crosstool' \ + -e '/common.windows/ r $(DOCKER_COMPOSITE_FOLDER_PATH)common.windows' \ + -e '/common.dockcross/ r $(DOCKER_COMPOSITE_FOLDER_PATH)common.dockcross' \ + -e '/common.label-and-env/ r $(DOCKER_COMPOSITE_FOLDER_PATH)common.label-and-env' \ + $< > $@ + +# +# web-wasm +# +web-wasm: web-wasm/Dockerfile + mkdir -p $@/imagefiles && cp -r imagefiles $@/ + cp -r test web-wasm/ + $(DOCKER) build -t $(ORG)/web-wasm:latest \ + -t $(ORG)/web-wasm:$(TAG) \ + --build-arg IMAGE=$(ORG)/web-wasm \ + --build-arg VCS_REF=`git rev-parse --short HEAD` \ + --build-arg VCS_URL=`git config --get remote.origin.url` \ + --build-arg BUILD_DATE=`date -u +"%Y-%m-%dT%H:%M:%SZ"` \ + web-wasm + rm -rf web-wasm/test + rm -rf $@/imagefiles + +web-wasm.test: web-wasm + cp -r test web-wasm/ + $(DOCKER) run $(RM) $(ORG)/web-wasm > $(BIN)/dockcross-web-wasm && chmod +x $(BIN)/dockcross-web-wasm + $(BIN)/dockcross-web-wasm python test/run.py --exe-suffix ".js" + rm -rf web-wasm/test + +# +# manylinux2014-aarch64 +# +manylinux2014-aarch64: manylinux2014-aarch64/Dockerfile + @# Register qemu + docker run --rm --privileged hypriot/qemu-register + @# Get libstdc++ from quay.io/pypa/manylinux2014_aarch64 container + docker run -v `pwd`:/host --rm -e LIB_PATH=/host/$@/xc_script/ quay.io/pypa/manylinux2014_aarch64 bash -c "PASS=1 /host/$@/xc_script/docker_setup_scrpits/copy_libstd.sh" + mkdir -p $@/imagefiles && cp -r imagefiles $@/ + $(DOCKER) build -t $(ORG)/manylinux2014-aarch64:latest \ + -t $(ORG)/manylinux2014-aarch64:$(TAG) \ + --build-arg IMAGE=$(ORG)/manylinux2014-aarch64 \ + --build-arg VCS_REF=`git rev-parse --short HEAD` \ + --build-arg VCS_URL=`git config --get remote.origin.url` \ + --build-arg BUILD_DATE=`date -u +"%Y-%m-%dT%H:%M:%SZ"` \ + -f manylinux2014-aarch64/Dockerfile . + rm -rf $@/imagefiles + @# libstdc++ is coppied into image, now remove it + docker run -v `pwd`:/host --rm quay.io/pypa/manylinux2014_aarch64 bash -c "rm -rf /host/$@/xc_script/usr" + +manylinux2014-aarch64.test: manylinux2014-aarch64 + $(DOCKER) run $(RM) $(ORG)/manylinux2014-aarch64 > $(BIN)/dockcross-manylinux2014-aarch64 \ + && chmod +x $(BIN)/dockcross-manylinux2014-aarch64 + $(BIN)/dockcross-manylinux2014-aarch64 /opt/python/cp38-cp38/bin/python test/run.py + +# +# manylinux2014-x64 +# +manylinux2014-x64: manylinux2014-x64/Dockerfile + mkdir -p $@/imagefiles && cp -r imagefiles $@/ + $(DOCKER) build -t $(ORG)/manylinux2014-x64:latest \ + -t $(ORG)/manylinux2014-x64:$(TAG) \ + --build-arg IMAGE=$(ORG)/manylinux2014-x64 \ + --build-arg VCS_REF=`git rev-parse --short HEAD` \ + --build-arg VCS_URL=`git config --get remote.origin.url` \ + --build-arg BUILD_DATE=`date -u +"%Y-%m-%dT%H:%M:%SZ"` \ + -f manylinux2014-x64/Dockerfile . + rm -rf $@/imagefiles + +manylinux2014-x64.test: manylinux2014-x64 + $(DOCKER) run $(RM) $(ORG)/manylinux2014-x64 > $(BIN)/dockcross-manylinux2014-x64 \ + && chmod +x $(BIN)/dockcross-manylinux2014-x64 + $(BIN)/dockcross-manylinux2014-x64 /opt/python/cp38-cp38/bin/python test/run.py + +# +# manylinux2014-x86 +# +manylinux2014-x86: manylinux2014-x86/Dockerfile + mkdir -p $@/imagefiles && cp -r imagefiles $@/ + $(DOCKER) build -t $(ORG)/manylinux2014-x86:latest \ + -t $(ORG)/manylinux2014-x86:$(TAG) \ + --build-arg IMAGE=$(ORG)/manylinux2014-x86 \ + --build-arg VCS_REF=`git rev-parse --short HEAD` \ + --build-arg VCS_URL=`git config --get remote.origin.url` \ + --build-arg BUILD_DATE=`date -u +"%Y-%m-%dT%H:%M:%SZ"` \ + -f manylinux2014-x86/Dockerfile . + rm -rf $@/imagefiles + +manylinux2014-x86.test: manylinux2014-x86 + $(DOCKER) run $(RM) $(ORG)/manylinux2014-x86 > $(BIN)/dockcross-manylinux2014-x86 \ + && chmod +x $(BIN)/dockcross-manylinux2014-x86 + $(BIN)/dockcross-manylinux2014-x86 /opt/python/cp38-cp38/bin/python test/run.py + +base: Dockerfile imagefiles/ + $(DOCKER) build -t $(ORG)/base:latest \ + -t $(ORG)/base:$(TAG) \ + --build-arg IMAGE=$(ORG)/base \ + --build-arg VCS_URL=`git config --get remote.origin.url` \ + . + +base.test: base + $(DOCKER) run $(RM) $(ORG)/base > $(BIN)/dockcross-base && chmod +x $(BIN)/dockcross-base + +# display +# +display_images: + for image in $(IMAGES); do echo $$image; done + +$(VERBOSE).SILENT: display_images + +# +# build implicit rule +# + +$(STANDARD_IMAGES): %: %/Dockerfile base + mkdir -p $@/imagefiles && cp -r imagefiles $@/ + $(DOCKER) build -t $(ORG)/$@:latest \ + -t $(ORG)/$@:$(TAG) \ + --build-arg IMAGE=$(ORG)/$@ \ + --build-arg VCS_REF=`git rev-parse --short HEAD` \ + --build-arg VCS_URL=`git config --get remote.origin.url` \ + --build-arg BUILD_DATE=`date -u +"%Y-%m-%dT%H:%M:%SZ"` \ + $@ + rm -rf $@/imagefiles + +clean: + for d in $(IMAGES) ; do rm -rf $$d/imagefiles ; done + for d in $(IMAGES) ; do rm -rf $(BIN)/dockcross-$$d ; done + for d in $(GEN_IMAGE_DOCKERFILES) ; do rm -f $$d ; done + rm -f Dockerfile + +purge: clean +# Remove all untagged images + $(DOCKER) container ls -aq | xargs -r $(DOCKER) container rm -f +# Remove all images with organization (ex dockcross/*) + $(DOCKER) images --filter=reference='$(ORG)/*' --format='{{.Repository}}:{{.Tag}}' | xargs -r $(DOCKER) rmi -f + +# Check bash syntax +bash-check: + find . -type f \( -name "*.sh" -o -name "*.bash" \) -print0 | xargs -0 -P"$(shell nproc)" -I{} \ + $(SHELLCHECK) --check-sourced --color=auto --format=gcc --severity=warning --shell=bash --enable=all "{}" + +# +# testing implicit rule +# +.SECONDEXPANSION: +$(addsuffix .test,$(STANDARD_IMAGES)): $$(basename $$@) + $(DOCKER) run $(RM) $(ORG)/$(basename $@) > $(BIN)/dockcross-$(basename $@) \ + && chmod +x $(BIN)/dockcross-$(basename $@) + $(BIN)/dockcross-$(basename $@) python3 test/run.py $($@_ARGS) + +# +# testing prerequisites implicit rule +# +test.prerequisites: + mkdir -p $(BIN) + +$(addsuffix .test,base $(IMAGES)): test.prerequisites + +.PHONY: base images $(IMAGES) test %.test clean purge bash-check display_images diff --git a/docs/1.0-Abstract.md b/docs/1.0-Abstract.md new file mode 100644 index 0000000..108e387 --- /dev/null +++ b/docs/1.0-Abstract.md @@ -0,0 +1,19 @@ +# Abstract + +Pronounce: PD I SEE + +Can you see the portable data iterface in this program ? + +[Why clang ?](https://clang.llvm.org/index.html) + +- Fast compiles and low memory use +- Expressive diagnostics (examples) +- GCC compatibility +- Modular library based architecture +- Support diverse clients (refactoring, static analysis, code generation, etc.) +- A real-world, production quality compiler +- A simple and hackable code base +- A single unified parser for C, Objective C, C++, and Objective C++ +- Conformance with C/C++/ObjC and their variants + +This project can see [PDI Portable Data Interface](https://gitlab.maisondelasimulation.fr/pdidev/pdi) representation in c/c++. diff --git a/docs/2.0-Requirement.md b/docs/2.0-Requirement.md new file mode 100644 index 0000000..d5259e4 --- /dev/null +++ b/docs/2.0-Requirement.md @@ -0,0 +1,15 @@ +# Usages + +## Feature + +|Work on :|C files|C++ files| +|:-:|:-:|:-:| +|Header|`.h`|`.hh` `.hxx` `.hpp`| +|Code|`.c`|`.cc` `.cxx` `.cpp`| + +## Installation + +Dependencies: + +- cmake +- make diff --git a/docs/2.1-Binary.md b/docs/2.1-Binary.md new file mode 100644 index 0000000..ef9cbd6 --- /dev/null +++ b/docs/2.1-Binary.md @@ -0,0 +1,37 @@ +# Binary + +The Binary is static: + +- LLVM embeded +- Clang embeded +- Libc embeded + +(build on ubuntu) + +Binary + +```sh +cmake -S . -DBINARY:BOOL=ON -B build && cmake --build build -- -j `nproc` +``` + +Google Test + +```sh +cmake -S . -DTEST:BOOL=ON -B build && cmake --build build -- -j `nproc` +cd build && ctest +``` + +```sh +./pdic ../PDI_test_parse.h -- +./pdic ../PDI_test_parse.h --stdout on -- # on/off +./pdic ../PDI_test_parse.h ../level_1.c --outputfilename out.yml -- +``` + +`--` is for [Compilation databases for Clang-based tools](https://eli.thegreenplace.net/2014/05/21/compilation-databases-for-clang-based-tools) +> Note that anything before the double dash “--” is an input to your LibTooling program, argv in main(), while anything after the double dash is an input to Clang itself (you won’t concern yourself with those). (source `LibTooling Example`) + +```sh +./pdic ../examples/level_1.c -- +``` + +pdic extract all information from [AST](https://en.wikipedia.org/wiki/Abstract_syntax_tree) and write PDI representation. diff --git a/docs/2.2-Python.md b/docs/2.2-Python.md new file mode 100644 index 0000000..7b9409e --- /dev/null +++ b/docs/2.2-Python.md @@ -0,0 +1,48 @@ +# Python Api + +Requirement: + +All dev requirements ([DevRequirement](/docs/2.0-Usages.md#Installation)) and: + +- libncursesw5 +- Clang / LLVM (all dev tools) + - libomp5 + - libc++-dev + - libclang-dev + - ... + +Installation example (ubuntu) + +Source: + +```sh +sudo apt update +sudo apt install libncursesw5 libpthread-stubs0-dev +sudo apt install clang-format clang-tidy clang-tools clang clangd libc++-dev libc++1 libc++abi-dev libc++abi1 libclang-dev libclang1 liblldb-dev libllvm-ocaml-dev libomp-dev libomp5 lld lldb llvm-dev llvm-runtime llvm python-clang +``` + +OR + +```sh +sudo bash -c "$(wget -O - https://apt.llvm.org/llvm.sh)" +# libclang-13-dev +``` + +> WARNING It may fail or somme dependency are not include (during pdic build or runtime) . Prefer the first command instead. + +## Python Test + +```sh +python3 setup.py build -G "Unix Makefiles" +python3 setup.py bdist_wheel -G "Unix Makefiles" -j `nproc` +python3 setup.py build_ext -G "Unix Makefiles" --inplace -j `nproc` +pytest +``` + +### pydoc + +```sh +pdoc3 pdic --http 0.0.0.0:8888 +# Or +pdoc3 pdic --html +``` diff --git a/docs/3.0-Developpement.md b/docs/3.0-Developpement.md new file mode 100644 index 0000000..042faab --- /dev/null +++ b/docs/3.0-Developpement.md @@ -0,0 +1,20 @@ +# Developpement + +Herre i present how to compile build and develop features. + +## Requirement + +- lib pthread (-lpthread) +- lib math (-lm) +- lib dynamic loader (-ldl) +- zlib (-lz) + +```sh +cmake -S . -B build +cmake --build build -- -j `nproc` +cd build && ctest +``` + +## Examples + +You can find differents examples -> [Here](./examples/) diff --git a/docs/4.0-Links.md b/docs/4.0-Links.md new file mode 100644 index 0000000..8453574 --- /dev/null +++ b/docs/4.0-Links.md @@ -0,0 +1,71 @@ +# Usefull links + +[Clang API](http://man.hubwiz.com/manual/Clang) + +## Clang / LLVM + +### Official documentation + +- [Clang Plugins doc](https://clang.llvm.org/docs/ClangPlugins.html) +- [Clang Driver Design](https://clang.llvm.org/docs/DriverInternals.html) +- [RecursiveASTVisitor Tutorial](https://clang.llvm.org/docs/RAVFrontendAction.html) + +### Other documentation + +- [Basics of AST manipulation](https://freecompilercamp.org/clang-AST-basics/) +- [Comment parser](https://danielbeard.io/2016/04/19/clang-frontend-action-part-1.html) +- [Hacking on Clang is surprisingly easy](https://mort.coffee/home/clang-compiler-hacking/) +- [understanding the clang ast](https://jonasdevlieghere.com/understanding-the-clang-ast/) +- [Clang Tutorial: The AST Matcher](https://xinhuang.github.io/posts/2015-02-08-clang-tutorial-the-ast-matcher.html) +- [Clang Tutorial: Finding Declarations](https://xinhuang.github.io/posts/2014-10-19-clang-tutorial-finding-declarations.html) + +### Clang Tutorial Part: LibTooling Example + +1. [Part I](https://kevinaboos.wordpress.com/2013/07/23/clang-tutorial-part-i-introduction/) +2. [Part II](https://kevinaboos.wordpress.com/2013/07/23/clang-tutorial-part-ii-libtooling-example/) +3. [Part III](https://kevinaboos.wordpress.com/2013/07/29/clang-tutorial-part-iii-plugin-example/) + +### libclang static + +- [libclang-static-build](https://github.com/deech/libclang-static-build) + +- [Cmake standalone-static-libclang](https://prozacchiwawa.medium.com/deechs-standalone-static-libclang-a-libclang-distro-that-works-187efdd4102e) + +### Pragma + +- [Pragma token kind identifier](https://repo.hca.bsc.es/gitlab/rferrer/llvm-epi-0.8/-/commit/ea4f7c776194c96f8ece1456bc22102c6cbc9a33) +- [Ex custom pragma 'TritonAssert'](https://github.com/quarkslab/clang/commit/0163f52f70e4781ce99710575bb66943125357b2) + +### Padding and packing in C/C++ + +- + +### Python Binding (pybind11) + +- https://pybind11.readthedocs.io/en/stable/cmake/index.html +- https://pybind11.readthedocs.io/en/stable/compiling.html#pybind11-add-module + +### Builder + +- https://scikit-build.readthedocs.io/en/latest/index.html +- https://github.com/scikit-build/scikit-build-sample-projects + +- https://github.com/scikit-build/scikit-ci +- https://github.com/scikit-build/scikit-ci/blob/master/docs/installation.rst + +- https://blog.kitware.com/creating-static-executables-on-linux/ +- https://cliutils.gitlab.io/modern-cmake/ + +### C/C++ Googletest & python3 unittest + +- https://google.github.io/googletest/ +- https://github.com/google/googletest/tree/master/googletest/samples + +- https://python-packaging-user-guide.readthedocs.io/ +- https://docs.python.org/3/library/unittest.html +- https://gayerie.dev/docs/python/python3/unittest.html + + +### Doc + +- https://squidfunk.github.io/mkdocs-material/ \ No newline at end of file diff --git a/docs/5.0-Extra.md b/docs/5.0-Extra.md new file mode 100644 index 0000000..7d3092a --- /dev/null +++ b/docs/5.0-Extra.md @@ -0,0 +1,34 @@ +# Extra + +## Install Clang llvm lld from source + +Follow: [LibTooling and LibASTMatchers](https://clang.llvm.org/docs/LibASTMatchersTutorial.html) installation tutorial if you want to contribute. + +Use `gold` or `lld` because `bfd` use too much ram + +`wget` or `curl --progress-bar -LO` + +```sh +wget https://github.com/llvm/llvm-project/archive/refs/tags/llvmorg-12.0.1.tar.gz +tar xvf llvmorg-12.0.1.tar.gz +cd llvm-project-llvmorg-12.0.1 + +cmake -G Ninja -S ./llvm -DLLVM_ENABLE_PROJECTS="clang;clang-tools-extra;lld" -DCMAKE_BUILD_TYPE=Release -DLLVM_USE_LINKER=gold -B build + +# export CC=`which clang` +# export CXX=`which clang++` +# export CFLAGS='-fuse-ld=ld.lld -rtlib=compiler-rt' +# export CXXFLAGS='-fuse-ld=lld' # gcc +# export CXXFLAGS='-B lld' # clang +# sudo update-alternatives --install "/usr/bin/ld" "ld" `which ld.lld` 10 + +cmake --build build -- -j`nproc` +sudo cmake --install build +cd build +ninja install +``` + +```sh +# uninstall from build +xargs rm < install_manifest.txt +``` diff --git a/docs/5.1-Documentations.md b/docs/5.1-Documentations.md new file mode 100644 index 0000000..704c74b --- /dev/null +++ b/docs/5.1-Documentations.md @@ -0,0 +1,10 @@ +# Documentation sevice + +```sh +mkdocs serve +docker run --rm -it -p 8000:8000 -v ${PWD}:/docs squidfunk/mkdocs-material +``` + +```bash +pdoc3 pdic +``` diff --git a/docs/index.md b/docs/index.md new file mode 120000 index 0000000..32d46ee --- /dev/null +++ b/docs/index.md @@ -0,0 +1 @@ +../README.md \ No newline at end of file diff --git a/docs_format/Roboto-Black.ttf b/docs_format/Roboto-Black.ttf new file mode 100644 index 0000000..2d45238 Binary files /dev/null and b/docs_format/Roboto-Black.ttf differ diff --git a/docs_format/Roboto-BlackItalic.ttf b/docs_format/Roboto-BlackItalic.ttf new file mode 100644 index 0000000..29a4359 Binary files /dev/null and b/docs_format/Roboto-BlackItalic.ttf differ diff --git a/docs_format/Roboto-Bold.ttf b/docs_format/Roboto-Bold.ttf new file mode 100644 index 0000000..d998cf5 Binary files /dev/null and b/docs_format/Roboto-Bold.ttf differ diff --git a/docs_format/Roboto-BoldItalic.ttf b/docs_format/Roboto-BoldItalic.ttf new file mode 100644 index 0000000..b4e2210 Binary files /dev/null and b/docs_format/Roboto-BoldItalic.ttf differ diff --git a/docs_format/Roboto-Italic.ttf b/docs_format/Roboto-Italic.ttf new file mode 100644 index 0000000..5b390ff Binary files /dev/null and b/docs_format/Roboto-Italic.ttf differ diff --git a/docs_format/Roboto-Light.ttf b/docs_format/Roboto-Light.ttf new file mode 100644 index 0000000..3526798 Binary files /dev/null and b/docs_format/Roboto-Light.ttf differ diff --git a/docs_format/Roboto-LightItalic.ttf b/docs_format/Roboto-LightItalic.ttf new file mode 100644 index 0000000..46e9bf7 Binary files /dev/null and b/docs_format/Roboto-LightItalic.ttf differ diff --git a/docs_format/Roboto-Medium.ttf b/docs_format/Roboto-Medium.ttf new file mode 100644 index 0000000..f714a51 Binary files /dev/null and b/docs_format/Roboto-Medium.ttf differ diff --git a/docs_format/Roboto-MediumItalic.ttf b/docs_format/Roboto-MediumItalic.ttf new file mode 100644 index 0000000..5dc6a2d Binary files /dev/null and b/docs_format/Roboto-MediumItalic.ttf differ diff --git a/docs_format/Roboto-Regular.ttf b/docs_format/Roboto-Regular.ttf new file mode 100644 index 0000000..2b6392f Binary files /dev/null and b/docs_format/Roboto-Regular.ttf differ diff --git a/docs_format/Roboto-Thin.ttf b/docs_format/Roboto-Thin.ttf new file mode 100644 index 0000000..4e797cf Binary files /dev/null and b/docs_format/Roboto-Thin.ttf differ diff --git a/docs_format/Roboto-ThinItalic.ttf b/docs_format/Roboto-ThinItalic.ttf new file mode 100644 index 0000000..eea836f Binary files /dev/null and b/docs_format/Roboto-ThinItalic.ttf differ diff --git a/entrypoint.sh b/entrypoint.sh new file mode 100755 index 0000000..f63f267 --- /dev/null +++ b/entrypoint.sh @@ -0,0 +1,4 @@ +# !/bin/bash --login +# activate conda environment and let the following process take over +source ~/.bashrc +exec "$@" \ No newline at end of file diff --git a/examples/PDI_test_parse.c b/examples/PDI_test_parse.c new file mode 100644 index 0000000..370061c --- /dev/null +++ b/examples/PDI_test_parse.c @@ -0,0 +1,42 @@ +#pragma pdi on + +typedef struct +{ + int my_int; + char my_char; +} var8; + +typedef struct +{ + char my_char; + int my_array[10][10]; +} var9; + +typedef struct +{ + char my_char; + var9 v9; +} var10; + +typedef struct +{ + char *my_pointer; + int my_int; +} var16; + +char var1; +short int var2; +int var3; +double var4; +float var5; +char var6[10]; +int var7[10000][10000]; + +int *var11; // pointer to int +int **var12; // pointer to int pointer +int **var13; // pointer to 32bit array of int +int *var14[32]; // array of int pointers + +var8 var15; + +#pragma pdi off \ No newline at end of file diff --git a/examples/__init__.py b/examples/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/examples/example.py b/examples/example.py new file mode 100644 index 0000000..36f49d0 --- /dev/null +++ b/examples/example.py @@ -0,0 +1,13 @@ +import os +import pathlib +import yaml +import pdic + +here = pathlib.Path(__file__).parent.resolve() +# pip3 install --use-feature=in-tree-build . -v +# pip show -f pdic +if __name__ == "__main__": + pdi_yml_description: str = pdic.files_to_pdi([os.path.join(here, "level_1.c"), + os.path.join(here, "level_2.c")]) + print(pdi_yml_description) + print(yaml.dump(yaml.load(pdi_yml_description))) diff --git a/examples/level_1.c b/examples/level_1.c index 7f4dbed..91c19ea 100644 --- a/examples/level_1.c +++ b/examples/level_1.c @@ -9,7 +9,7 @@ * ----- * Licenses: EUPL * ----- - * Copyright 2021 - 2021 NinjaTech + * Copyright 2021 - 2021 neudinger */ short notsee; diff --git a/examples/level_2.c b/examples/level_2.c index 9f39af6..dbb97ad 100644 --- a/examples/level_2.c +++ b/examples/level_2.c @@ -9,7 +9,7 @@ * ----- * Licenses: EUPL * ----- - * Copyright 2021 - 2021 NinjaTech + * Copyright 2021 - 2021 neudinger */ #pragma pdi on diff --git a/examples/level_3.cpp b/examples/level_3.cpp index 4d88dab..fac0410 100644 --- a/examples/level_3.cpp +++ b/examples/level_3.cpp @@ -9,7 +9,7 @@ * ----- * Licenses: EUPL * ----- - * Copyright 2021 - 2021 NinjaTech + * Copyright 2021 - 2021 neudinger */ #pragma pdi on diff --git a/mkdocs.yml b/mkdocs.yml new file mode 100644 index 0000000..2312065 --- /dev/null +++ b/mkdocs.yml @@ -0,0 +1,65 @@ +# Project information +site_name: PDIC +site_description: 'Interface description language c/c++ to PDI' +site_author: 'Barre Kevin' +# site_url: '' +# material, readthedocs, bootstrap4 + +theme: + features: + - navigation.instant + name: 'material' + language: 'fr' + palette: + primary: 'blue grey' + accent: 'teal' + font: + text: 'Ubuntu' + code: 'Ubuntu Mono' + extra: + disqus: 'Barre kevin' + version: + default: stable + +markdown_extensions: + - admonition + - meta + - codehilite: + linenums: true + guess_lang: false + - footnotes + - toc: + permalink: "#" + - pymdownx.arithmatex + - pymdownx.betterem: + smart_enable: all + - pymdownx.caret + - pymdownx.critic + - pymdownx.details + - pymdownx.emoji: + emoji_generator: python/name:pymdownx.emoji.to_svg + - pymdownx.inlinehilite + - pymdownx.magiclink + - pymdownx.mark + - pymdownx.smartsymbols + - pymdownx.superfences + - pymdownx.tasklist: + custom_checkbox: true + - pymdownx.tilde + +nav: + - Abstract: '1.0-Abstract.md' + - Usage: + - Requirement: '2.0-Requirement.md' + - Binary: '2.1-Binary.md' + - Python: '2.2-Python.md' + - Developpement: '3.0-Developpement.md' + - Links: 4.0-Links.md + - Extra: 5.0-Extra.md + - Documentations: 5.1-Documentations.md +# plugins: +# - redirects: +# redirect_maps: +# ./docs/1.0-Abstract.md: 1.0-Abstract.md +# # 'old/file.md': 'new/file.md' +# # 'some_file.md': 'http://external.url.com/foobar' diff --git a/modules/Binary.cmake b/modules/Binary.cmake new file mode 100644 index 0000000..62bffa6 --- /dev/null +++ b/modules/Binary.cmake @@ -0,0 +1,21 @@ +add_compile_options(-fno-rtti) +set(CLANG_LIB clang_bundled) # static +set(CMAKE_FIND_LIBRARY_SUFFIXES .a) +option(BUILD_SHARED_LIBS "Build using shared libraries" OFF) +set(CMAKE_EXE_LINKER_FLAGS -static) +message(STATUS "The project will build a binary") +set(BINTARGET "${TARGET}.static") +add_executable(${BINTARGET} + ${SRC} +) +target_link_libraries(${BINTARGET} + PRIVATE + ${CLANG_LIB} # clang lib + pthread # multithreading + m # math + dl # dynamic loader + z # zlib +) +add_dependencies(${BINTARGET} libclang-static-build) +set_property(TARGET ${BINTARGET} PROPERTY CXX_STANDARD 20) +unset(CLANG_LIB CACHE) \ No newline at end of file diff --git a/modules/Builder.cmake b/modules/Builder.cmake new file mode 100644 index 0000000..17f15ad --- /dev/null +++ b/modules/Builder.cmake @@ -0,0 +1,39 @@ +# IF MSVC +# set(CMAKE_FIND_LIBRARY_SUFFIXES ".lib") +# SET(CMAKE_FIND_LIBRARY_SUFFIXES ".a;.dll.a;.dll") +# target_compile_options(your_target_name [PUBLIC|PRIVATE] /MT) +# target_link_options(your_target_name [PUBLIC|PRIVATE] /INCREMENTAL:NO /NODEFAULTLIB:MSVCRT) +# IF MFC +# https://cmake.org/cmake/help/latest/variable/CMAKE_MFC_FLAG.html +# set(CMAKE_MFC_FLAG 1) + + +if(SKBUILD) # Python pybind build + include(Pybuild) +endif(SKBUILD) + +if(BINARY OR TEST) # pdic portable binary + include(LibClang) + message(STATUS "clang binary_dir is " ${binary_dir}) + link_directories( + ${binary_dir} # some lib are there + ${binary_dir}/_all_archives # other are there + ${binary_dir}/libclang-copied/lib # all are copied there + ) + + include_directories( + ${binary_dir}/_deps/clang_sources-src/include + ${binary_dir}/_deps/libclang_prebuilt-src/include + ) + + if(BINARY) # pdic portable binary + include(Binary) + endif(BINARY) + if(TEST) # cmake google test + include(Tests) + endif(TEST) +endif(BINARY OR TEST) + + + + diff --git a/modules/LibClang.cmake b/modules/LibClang.cmake new file mode 100644 index 0000000..4123cf2 --- /dev/null +++ b/modules/LibClang.cmake @@ -0,0 +1,12 @@ +include(ExternalProject) +# Clang 12 +set(LIBCLANG_INSTALL libclang-copied) +ExternalProject_Add(libclang-static-build + DOWNLOAD_DIR "libclang-static-build" + INSTALL_DIR "${LIBCLANG_INSTALL}" + GIT_REPOSITORY "https://github.com/deech/libclang-static-build" + GIT_TAG "3daf7793644764bff2dad11021051a6b8f450a52" # latest + # GIT_TAG "0cae8e85ef1ad951e1bb560e1eadcd64b2f0828e" + CMAKE_ARGS "-DCMAKE_INSTALL_PREFIX=${LIBCLANG_INSTALL}" +) +ExternalProject_Get_Property(libclang-static-build binary_dir) diff --git a/modules/Pybuild.cmake b/modules/Pybuild.cmake new file mode 100644 index 0000000..10a4bee --- /dev/null +++ b/modules/Pybuild.cmake @@ -0,0 +1,48 @@ +# Python pybind build + +FetchContent_Declare( + pybind11 + URL https://github.com/pybind/pybind11/archive/refs/tags/v2.7.1.tar.gz +) +FetchContent_MakeAvailable(pybind11) +message(STATUS "The project is built using scikit-build") +set(PYTARGET "py${TARGET}") +# export LLVM_DIR=/media/kbarre/1d7f0a7c-34d5-4837-ad9a-95be3ec8cb53/Documents/dev/desciptor/pdic/src/llvm-project-llvmorg-12.0.1/build/ +# export Clang_DIR=/media/kbarre/1d7f0a7c-34d5-4837-ad9a-95be3ec8cb53/Documents/dev/desciptor/pdic/src/llvm-project-llvmorg-12.0.1/build/ +find_package(LLVM REQUIRED) +find_package(Clang REQUIRED) +list(APPEND CMAKE_MODULE_PATH "${LLVM_CMAKE_DIR}") +set(LLVM_ENABLE_PLUGINS ON) +set(LLVM_LINK_COMPONENTS Support) +# # Set compiler flags. +# include(HandleLLVMOptions) # Hardcore warning +# # Use LLVM and clang headers. +include(AddLLVM) +include_directories(${LLVM_INCLUDE_DIRS}) +include_directories(${CLANG_INCLUDE_DIRS}) + +add_definitions(${LLVM_DEFINITIONS}) +add_definitions(${CLANG_DEFINITIONS}) + +pybind11_add_module(${PYTARGET} + MODULE + ${SRC} +) +target_link_libraries(${PYTARGET} + PRIVATE + clang + clangAST + clangBasic + clangFrontend + clangTooling + clangASTMatchers + clangSerialization + pthread # multithreading + m # math + dl # dynamic loader + z # zlib +) +add_compile_definitions(SKBUILD=True) +set_property(TARGET ${PYTARGET} PROPERTY CXX_STANDARD 20) +# add_dependencies(${PYTARGET} libclang-static-build) +install(TARGETS ${PYTARGET} LIBRARY DESTINATION .) diff --git a/modules/Tests.cmake b/modules/Tests.cmake new file mode 100644 index 0000000..f80e369 --- /dev/null +++ b/modules/Tests.cmake @@ -0,0 +1,43 @@ +FetchContent_Declare( + googletest + URL https://github.com/google/googletest/archive/refs/tags/release-1.11.0.tar.gz +) +message(STATUS "The project will build google tests") +set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) +FetchContent_MakeAvailable(googletest) +add_compile_options(-fno-rtti) + +list(APPEND FileName c_yaml_test) + +add_compile_definitions(GOOGLE_TEST=True) + + +get_filename_component(WORKDIR "${CMAKE_CURRENT_LIST_DIR}" PATH) +set(WORKDIR ${WORKDIR}/src/pdic) +include_directories( + ${WORKDIR} +) + +include(GoogleTest) +enable_testing() +foreach(X IN LISTS FileName) + message(STATUS "Build TEST: ${X}") + add_executable( + ${X} + tests/${X}.cc + ${SRC} + ) + target_link_libraries(${X} + PRIVATE + gtest_main # Google test lib + clang_bundled # Dynamic clang lib + pthread # multithreading + m # math + dl # dynamic loader + z # zlib + ) + add_dependencies(${X} libclang-static-build) + set_property(TARGET ${X} PROPERTY CXX_STANDARD 20) + gtest_discover_tests(${X}) + add_test(NAME ${X} COMMAND ${X}) +endforeach() \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..8410104 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,5 @@ +[build-system] +requires = ["setuptools", "wheel", "scikit-build", "cmake", "ninja", "pytest"] +build-backend = "setuptools.build_meta" +[tool.pytest.ini_options] +testpaths = ["tests"] diff --git a/requirement.yml b/requirement.yml new file mode 100644 index 0000000..733cfa7 --- /dev/null +++ b/requirement.yml @@ -0,0 +1,13 @@ +channels: + - defaults + - conda-forge +dependencies: + - python=3.9 + - pip + - cmake=3.21.2 + - scikit-build + - twine + - pytest + - pyyaml + - pip: + - "pdoc3" diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..7342fae --- /dev/null +++ b/setup.py @@ -0,0 +1,69 @@ +__author__ = "Barre Kevin" +__maintainer__ = "Barre kevin" +__credits__ = ["https://github.com/neudinger"] +__email__ = "kevin.barre@epitech.eu" +__status__ = "Beta" + +import os +import sys +import pathlib +import re +try: + from skbuild import setup +except ImportError: + print('Please update pip, you need pip 10 or greater,\n' + ' or you need to install the PEP 518 requirements in pyproject.toml yourself', file=sys.stderr) + raise + +here = pathlib.Path(__file__).parent.resolve() +long_description = (here / "README.md").read_text(encoding="utf-8") + + +version = os.getenv("version", default="0.0.1") +version = re.match(r".*?(\d+\.?\d+\.?\d+).*?", + version, re.S).groups()[0] + + +print(f"__version__ = '{version}'", + file=open(f"{here}/src/pdic/__version__.py", "w")) + +setup( + name="pdic", + version=version, + author_email="kevin.barre@epitech.eu", + description="Python api (IDL) Transpiler Source-to-source compiler C/C++ to YAML Description based on clang", + long_description_content_type="text/markdown", + long_description=long_description, + author="Barre Kevin", + license="EUPL", + python_requires=">=3.6", + packages=["pdic"], + package_dir={"": "src"}, + project_urls={ + 'Source': 'https://github.com/neudinger/pdic', + }, + cmake_install_dir="src/pdic", + cmake_args=['-DDEBUG:BOOL=OFF'], + install_requires=[ + "pyyaml" + ], + classifiers=[ + 'Development Status :: 5 - Production/Stable', + 'Intended Audience :: Developers', + "Programming Language :: C++", + "Topic :: Software Development", + "Topic :: Scientific/Engineering", + "Typing :: Typed", + "Operating System :: POSIX", + "Operating System :: Unix", + "Operating System :: MacOS", + "Programming Language :: Python :: 3", + "Operating System :: OS Independent", + 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3.6', + 'Programming Language :: Python :: 3.7', + 'Programming Language :: Python :: 3.8', + 'Programming Language :: Python :: 3.9', + 'Programming Language :: Python :: 3 :: Only', + ], +) diff --git a/src/pdic/PDIDecls.cpp b/src/pdic/PDIDecls.cpp new file mode 100644 index 0000000..e1e1833 --- /dev/null +++ b/src/pdic/PDIDecls.cpp @@ -0,0 +1,251 @@ +/* + * File: PDIDecls.cpp + * Project: PDIC + * File Created: Monday, 10th May 2021 12:23:33 am + * Author: kbarre (kevin.barre@epitech.eu) + * ----- + * Last Modified: Thursday, 22nd July 2021 12:57:01 am + * Modified By: kbarre (kevin.barre@epitech.eu>) + * ----- + * Licenses: EUPL + * ----- + * Copyright 2021 - 2021 neudinger + */ + +// clang::ASTFrontendAction +#include "clang/AST/RecursiveASTVisitor.h" + +// import clang::CompilerInstance +#include "clang/Frontend/CompilerInstance.h" + +// import clang::tooling::ClangTool +#include "clang/Tooling/Tooling.h" + +// import clang::tooling::CommonOptionsParser +// #include "clang/Tooling/CommonOptionsParser.h" $ only for pdi binary + +// use of clang::ASTRecordLayout &typeLayout +#include "clang/AST/RecordLayout.h" + +#include "PDIDecls.hpp" + +namespace PDI +{ + // Usefull link + // https://github.com/quarkslab/clang/commit/0163f52f70e4781ce99710575bb66943125357b2 + + /// push the declaration of __attribute(annotate(char const*))) into the stream + /// it is added before each call + static void PragmaPDItoAnnotate(clang::Preprocessor &PP, + clang::SmallVectorImpl &TokenList, + std::string AnnotateStr) + { + static char const _attrAnnotate[] = "annotate"; + clang::Token AttrNameTok; + AttrNameTok.startToken(); + AttrNameTok.setKind(clang::tok::string_literal); + AttrNameTok.setLength(AnnotateStr.size()); + AttrNameTok.setLiteralData(strdup(AnnotateStr.data())); + + clang::Token AttrTok; + AttrTok.startToken(); + AttrTok.setKind(clang::tok::kw___attribute); + + clang::Token LParTok; + LParTok.startToken(); + LParTok.setKind(clang::tok::l_paren); + + clang::Token RParTok; + RParTok.startToken(); + RParTok.setKind(clang::tok::r_paren); + + clang::Token AnnotTok; + AnnotTok.startToken(); + AnnotTok.setKind(clang::tok::identifier); + AnnotTok.setIdentifierInfo(PP.getIdentifierInfo(_attrAnnotate)); + + TokenList.push_back(AttrTok); + TokenList.push_back(LParTok); + TokenList.push_back(LParTok); + TokenList.push_back(AnnotTok); + TokenList.push_back(LParTok); + TokenList.push_back(AttrNameTok); + TokenList.push_back(RParTok); + TokenList.push_back(RParTok); + TokenList.push_back(RParTok); + } + void PragmaHandler::HandlePragma(clang::Preprocessor &PP, + clang::PragmaIntroducer Introducer, + clang::Token &PragmaTok) + { + clang::Token Tok; + clang::SmallVector TokenList; + std::ostringstream PDIDirectiveStream; + + PDIDirectiveStream << "\""; + + while ((PP.Lex(Tok), true) && Tok.isNot(clang::tok::eod)) + PDIDirectiveStream << PP.getSpelling(Tok); + if (pdiSwitch.isRaised(/* stringStream= */ PDIDirectiveStream, + /* sourceLocation= */ Tok.getLocation())) + return; + + PDIDirectiveStream << "\""; + + PragmaPDItoAnnotate(PP, TokenList, PDIDirectiveStream.str()); + clang::Token *TokenArray = new clang::Token[TokenList.size()]; + std::copy(TokenList.begin(), TokenList.end(), TokenArray); + PP.EnterTokenStream(/* Toks= */ llvm::makeArrayRef(TokenArray, TokenList.size()), + /* DisableMacroExpansion= */ false, + /* IsReinject= */ true); + // Do NOT delete TokenArray + // It will remove AST information + } + const bool PDIDeclVisitor::VisitVarDecl(const clang::VarDecl *varDecl) + { + typeIdentifiers[varDecl->getNameAsString()] = EvalDecl(varDecl); + return !PragmaError; + } + const bool PDIDeclVisitor::VisitRecordDecl(const clang::RecordDecl *recordDecl) + { + const clang::ASTRecordLayout &typeLayout(recordDecl->getASTContext().getASTRecordLayout(recordDecl)); + StructAttr curentStruct = StructAttr(/* name= */ recordDecl->getNameAsString(), + /* size= */ typeLayout.getSize(), + /* fieldCount= */ typeLayout.getFieldCount(), + /* sourceLocation= */ recordDecl->getLocation()); + + if (!recordDecl->isStruct()) + RAISE("Only struct allowed", recordDecl); + + if (recordDecl->getNameAsString().empty()) + WARN("Anonymous struct used : First alias Or generated ID will be used as StructName", recordDecl); + + // Check if + // #pragma pack + // #pragma pack(n) + // https://docs.microsoft.com/en-us/cpp/preprocessor/pack?view=msvc-160 + // and __attribute__((packed)) + if (const auto *MFAA = recordDecl->getAttr()) + curentStruct.packed = true; + else if (recordDecl->getAttr()) + curentStruct.packed = true; + + structIdentifiers[reinterpret_cast(recordDecl->getTypeForDecl())] = curentStruct; + + // return true if no PragmaError was raised + // This method must return true if no error occurred + // PragmaError must alway be false if no error occurred + return !PragmaError; + } + const bool PDIDeclVisitor::VisitFieldDecl(const clang::FieldDecl *fieldDecl) + { + const auto recordAddr = reinterpret_cast(fieldDecl->getParent()->getTypeForDecl()); + auto &curentStruct = structIdentifiers[recordAddr]; + const clang::ASTRecordLayout &typeLayout = fieldDecl->getASTContext().getASTRecordLayout(fieldDecl->getParent()); + TypeAttr curentType = EvalDecl(fieldDecl); + + if (!fieldDecl->getParent()->isStruct()) + RAISE("Only struct type are suported", fieldDecl); + + if (wrapMap(structIdentifiers).hasNot(/* key= */ recordAddr)) + RAISE("Parent record type are not suported", fieldDecl); + + // Add offset on each field base on padding and packing struct memory alignement + curentType.offset = typeLayout.getFieldOffset(fieldDecl->getFieldIndex()) / BYTE_SIZE; + + // Add Field description to current struct description + curentStruct.fields[fieldDecl->getNameAsString()] = curentType; + + // return true if no PragmaError was raised + // This must return true if no error occurred + // PragmaError must alway be false if no error occurred + return !PragmaError; + } + const bool PDIDeclVisitor::VisitTypedefDecl(const clang::TypedefDecl *typedefDecl) + { + // Retreive struct inserted during VisitRecordDecl process + auto &curentStruct = structIdentifiers[reinterpret_cast(typedefDecl->getTypeSourceInfo()->getType()->getAs())]; + // Define this alias as global name if struct is Anonymous + if (curentStruct.name.empty()) + curentStruct.name = typedefDecl->getNameAsString(); + // Add typedef alias to this struct description + curentStruct.alias.push_back(typedefDecl->getNameAsString()); + return true; + } + bool ASTConsumerDeclConsumer::HandleTopLevelDecl(clang::DeclGroupRef dg) + { + for (clang::Decl *decl : dg) + { + /// Create Custom annotation + // decl->addAttr(clang::AnnotateAttr::CreateImplicit(decl->getASTContext(), + // pragmaTypeIdentifiers.type)); + + /// Check if the current declaration is between pragma on and off + /// pragma pdi on + /// (decl) + /// pragma pdi off + if (!(pdiSwitch.isOnRaised && IsBefore(/* LHS= */ pdiSwitch.pdiOnLoc, + /* RHS= */ decl->getLocation())) || + (pdiSwitch.isOffRaised && IsBefore(/* LHS= */ pdiSwitch.pdiOffLoc, + /* RHS= */ decl->getLocation()))) + break; +#ifdef DEBUG + decl->dumpColor(); // <- Usefull for debug +#endif + /// Stop if any error occurred + if (!declNodeVisitor.TraverseDecl(decl) || + compilerInstance.getSourceManager().getDiagnostics().hasErrorOccurred()) + return false; + } + return true; + } +} + +class PDIPluginASTAction : public clang::ASTFrontendAction +{ +public: + std::unique_ptr + CreateASTConsumer(clang::CompilerInstance &compilerInstance, + const llvm::StringRef fileName) override + { + clang::Preprocessor &PP = compilerInstance.getPreprocessor(); + PP.AddPragmaHandler(new PDI::PragmaHandler()); + return std::make_unique(compilerInstance); + } +}; + +void serializeIn(std::ostringstream &ostream) +{ + if (!PDI::structIdentifiers.empty()) + ostream << "structs:\n"; + DescribeStructs(ostream, PDI::structIdentifiers); + if (!PDI::typeIdentifiers.empty()) + ostream << "data:\n"; + DescribeTypes(ostream, + PDI::typeIdentifiers, + PDI::structIdentifiers); +} + +void writeYML(const std::ostringstream &ostream, + const std::string &firstSourceFile) +{ + std::string outputFileName(firstSourceFile); + std::ofstream outputFile(outputFileName + .substr(0, outputFileName.find_last_of('.')) + + ".yml", + std::ios::trunc | std::ios::out); + outputFile << ostream.str(); + outputFile.close(); +} + +// clang::tooling::runToolOnCode(std::make_unique(), "namespace n { namespace m { class C {}; } }"); +// std::unique_ptr AST(tooling::buildASTFromCode("auto x = 1 + 1;")); +// TranslationUnitDecl *DC = AST->getASTContext().getTranslationUnitDecl(); + +#if defined(SKBUILD) +#include "pypdicbind.hpp" +#elif defined(GOOGLE_TEST) +#include "maintest.hpp" +#else +#include "main.hpp" +#endif // SKBUILD \ No newline at end of file diff --git a/src/pdic/PDIDecls.hpp b/src/pdic/PDIDecls.hpp new file mode 100644 index 0000000..f0356db --- /dev/null +++ b/src/pdic/PDIDecls.hpp @@ -0,0 +1,115 @@ +/* + * File: PDIDecls.hpp + * Project: PDIC + * File Created: Wednesday, 21st July 2021 4:34:34 pm + * Author: kbarre (kevin.barre@epitech.eu) + * ----- + * Last Modified: Thursday, 22nd July 2021 12:57:37 am + * Modified By: kbarre (kevin.barre@epitech.eu>) + * ----- + * Licenses: EUPL + * ----- + * Copyright 2021 - 2021 neudinger + */ + +#if !defined(PDI_DECLS) +#define PDI_DECLS +#include "PDITools.hpp" +#include "PDISerializer.hpp" + +namespace PDI +{ + static bool PragmaError = false; + static TypeMap typeIdentifiers; + static StructMap structIdentifiers; + static Switch pdiSwitch; + + class PragmaHandler : public clang::PragmaHandler + { + public: + PragmaHandler() : clang::PragmaHandler() {} + + void HandlePragma(clang::Preprocessor &PP, + clang::PragmaIntroducer Introducer, + clang::Token &PragmaTok) override; + }; + + // RecursiveASTVisitor does a pre-order depth-first traversal of the + // AST. We implement VisitFoo() methods for the types of nodes we are + // interested in. + class PDIDeclVisitor : public clang::RecursiveASTVisitor + { + private: + const clang::SourceManager &sourceManager; + template + TypeAttr EvalDecl(DECL nodeDecl) + { + + TypeAttr curentId; + std::vector declTypes; + const std::string declString(nodeDecl->getType().getAsString()); + const clang::TypeInfo typeInfo = nodeDecl->getASTContext().getTypeInfo(nodeDecl->getType()); + + if (declString.find("struct (") != std::string::npos) + RAISE("Anonymous struct without typedef not allowed", nodeDecl); + + if (nodeDecl->getType()->template getAs()->isStructureType()) + curentId.structId = reinterpret_cast(nodeDecl->getType()->template getAs()); + + curentId.sourceLoc = nodeDecl->getLocation(); + curentId.name = nodeDecl->getNameAsString(); + split(declString, declTypes); + curentId.type = (declTypes.size() > 1) ? *(--(--declTypes.cend())) : declTypes.back(); + curentId.starsNbr = charLen(declString, '*'); + extractBetweens(declTypes.back(), curentId.arraySizes); + for (const auto &attr : nodeDecl->getAttrs()) + // llvm::outs() << attr->getSpelling() << "\n" // usefull in debug mode + if (attr->getKind() == clang::attr::Annotate) + curentId.pragma.load(/* attributes= */ + (static_cast(attr))->getAnnotation().str()); + if (curentId.starsNbr && + curentId.pragma.arraySizes.size() >= curentId.starsNbr) + RAISE("Numbers of stars must be > in Pragma array", nodeDecl); + + curentId.buffersize = typeInfo.Width / BYTE_SIZE; + + return curentId; + } + + public: + explicit PDIDeclVisitor(const clang::SourceManager &SM) : sourceManager(SM) {} + const bool VisitVarDecl(const clang::VarDecl *varDecl); + // Struct handler + const bool VisitRecordDecl(const clang::RecordDecl *recordDecl); + const bool VisitFieldDecl(const clang::FieldDecl *fieldDecl); + // This will add any alias to the existing structures + const bool VisitTypedefDecl(const clang::TypedefDecl *typedefDecl); + }; + + // An ASTConsumer is a client object that receives callbacks as the AST is + // built, and "consumes" it. + class ASTConsumerDeclConsumer : public clang::ASTConsumer + { + private: + PDIDeclVisitor declNodeVisitor; + clang::CompilerInstance &compilerInstance; + + /// Return true If LHS is located before RHS + // constexpr + bool const IsBefore(const clang::SourceLocation &LHS, + const clang::SourceLocation RHS) + { + return compilerInstance.getSourceManager().isBeforeInTranslationUnit(LHS, RHS); + } + + public: + explicit ASTConsumerDeclConsumer(clang::CompilerInstance &CI) + : declNodeVisitor(PDIDeclVisitor(CI.getSourceManager())), + compilerInstance(CI) {} + + // Called by the parser for each top-level declaration group. + // Returns true to continue parsing, or false to abort parsing. + virtual bool HandleTopLevelDecl(clang::DeclGroupRef dg) override; + }; +} +#endif // PDI_DECLS diff --git a/src/pdic/PDISerializer.cpp b/src/pdic/PDISerializer.cpp new file mode 100644 index 0000000..39a2d07 --- /dev/null +++ b/src/pdic/PDISerializer.cpp @@ -0,0 +1,119 @@ +/* + * File: PDISerializer.cpp + * Project: PDIC + * File Created: Wednesday, 21st July 2021 11:54:17 am + * Author: kbarre (kevin.barre@epitech.eu) + * ----- + * Last Modified: Thursday, 22nd July 2021 12:57:43 am + * Modified By: kbarre (kevin.barre@epitech.eu>) + * ----- + * Licenses: EUPL + * ----- + * Copyright 2021 - 2021 neudinger + */ + +#include "PDISerializer.hpp" + +void DescribeStruct(std::ostringstream &ostream, + const PDI::StructAttr &structIdentifier, + const PDI::StructMap &structIdentifiers, + const std::size_t extraSpaces) +{ + ostream << duplicateChar(' ', extraSpaces) << "type: record\n"; + ostream << duplicateChar(' ', extraSpaces) << "name: " << structIdentifier.name << "\n"; + if (!structIdentifier.alias.empty()) + { + ostream << duplicateChar(' ', extraSpaces) << "alias: ["; + std::copy(structIdentifier.alias.cbegin(), structIdentifier.alias.cend() - 1, + std::ostream_iterator(ostream, ", ")); + ostream << structIdentifier.alias.back() << "]\n"; + } + ostream << duplicateChar(' ', extraSpaces) << "fieldsize: " << structIdentifier.fieldSize << "\n"; + ostream << duplicateChar(' ', extraSpaces) << "buffersize: " << structIdentifier.size.getQuantity() << "\n"; + ostream << duplicateChar(' ', extraSpaces) << "packed: " << structIdentifier.packed << "\n"; + ostream << duplicateChar(' ', extraSpaces) << "members:\n"; + DescribeTypes(ostream, + structIdentifier.fields, + structIdentifiers, + extraSpaces + 1); +} + +void DescribeStructs(std::ostringstream &ostream, + const PDI::StructMap &structIdentifiers, + const std::size_t extraSpaces) +{ + for (const auto &[structID, structIdentifier] : structIdentifiers) + { + ostream << duplicateChar(' ', extraSpaces) << structIdentifier.name << ":\n"; + DescribeStruct(ostream, + structIdentifier, + structIdentifiers, + extraSpaces + 1); + } +} + +void DescribeTypes(std::ostringstream &ostream, + const PDI::TypeMap &typeIdentifiers, + const PDI::StructMap &structIdentifiers, + const std::size_t extraSpaces) +{ + std::string typeRepr; + for (const auto &[typeIdentifierName, typeIdentifier] : typeIdentifiers) + { + pushCharsIn(ostream, ' ', extraSpaces); + ostream << typeIdentifier.name << ':'; + typeRepr = typeIdentifier.type; + + // Dont print offset multiple time + if (typeIdentifier.offset && + !typeIdentifier.starsNbr && // PDI::describePointer print offset + typeIdentifier.arraySizes.empty() && // PDI::describeArray print offset + typeIdentifier.pragma.arraySizes.empty() // PDI::describeArray print offset + ) + { + ostream << "\n"; + pushCharsIn(ostream, ' ', extraSpaces + 1); + ostream << "offset: " << typeIdentifier.offset; + } + if (typeIdentifier.pragma.isNotEmpty()) + { + if (!typeIdentifier.pragma.type.empty()) + typeRepr = typeIdentifier.pragma.type; + if (!typeIdentifier.pragma.arraySizes.empty()) + PDI::describePragmaArray(typeIdentifier, typeRepr); + if (typeIdentifier.starsNbr) + PDI::describePointer(typeIdentifier, typeRepr); + if (!typeIdentifier.arraySizes.empty()) + PDI::describeArray(typeIdentifier.arraySizes, typeRepr, typeIdentifier.offset); + if (typeRepr == typeIdentifier.pragma.type) + typeRepr = "\n" + + duplicateChar(' ', extraSpaces + 1) + "type: " + typeRepr; + ostream << typeRepr; + } + else + { + if (typeIdentifier.starsNbr) + PDI::describePointer(typeIdentifier, typeRepr); + if (!typeIdentifier.arraySizes.empty()) + PDI::describeArray(typeIdentifier.arraySizes, typeRepr); + if (typeIdentifier.structId) + { + ostream << "\n"; + DescribeStruct(ostream, + wrapMap(structIdentifiers).get(typeIdentifier.structId, PDI::StructAttr()), + structIdentifiers, + extraSpaces + 1); + typeRepr.clear(); + } + else if (typeIdentifier.type == typeRepr) + { + pushCharsIn(ostream, ' ', extraSpaces); + typeRepr = "\n" + + duplicateChar(' ', extraSpaces + 1) + "type: " + typeRepr; + } + ostream << typeRepr; + } + ostream << "\n"; + typeRepr.clear(); + } +} \ No newline at end of file diff --git a/src/pdic/PDISerializer.hpp b/src/pdic/PDISerializer.hpp new file mode 100644 index 0000000..defeda1 --- /dev/null +++ b/src/pdic/PDISerializer.hpp @@ -0,0 +1,50 @@ +/* + * File: PDISerializer.hpp + * Project: PDIC + * File Created: Wednesday, 21st July 2021 11:55:12 am + * Author: kbarre (kevin.barre@epitech.eu) + * ----- + * Last Modified: Thursday, 22nd July 2021 12:57:51 am + * Modified By: kbarre (kevin.barre@epitech.eu>) + * ----- + * Licenses: EUPL + * ----- + * Copyright 2021 - 2021 neudinger + */ + +#if !defined(PDI_SERIALIZER) +#define PDI_SERIALIZER +#include "PDITools.hpp" + +void DescribeStructs(std::ostringstream &ostream, + const PDI::StructMap &structIdentifiers, + const std::size_t extraSpaces = 1); + +void DescribeStruct(std::ostringstream &ostream, + const PDI::StructAttr &structIdentifier, + const PDI::StructMap &structIdentifiers, + const std::size_t extraSpaces = 1); + +void DescribeTypes(std::ostringstream &ostream, + const PDI::TypeMap &typeIdentifiers, + const PDI::StructMap &structIdentifiers, + const std::size_t extraSpaces = 1); + +inline void pushCharsIn(std::ostringstream &ostream, + const char &c, + const std::size_t &nb = 1) noexcept +{ + for (size_t i = 0; i < nb; i++) + ostream << c; +} + +inline std::string duplicateChar(const char &c, + const std::size_t &nb = 1) noexcept +{ + std::ostringstream stream; + for (size_t i = 0; i < nb; i++) + stream << c; + return stream.str(); +} + +#endif // PDI_SERIALIZER diff --git a/src/pdic/PDITools.cpp b/src/pdic/PDITools.cpp new file mode 100644 index 0000000..09b5b49 --- /dev/null +++ b/src/pdic/PDITools.cpp @@ -0,0 +1,217 @@ +/* + * File: PDITools.cpp + * Project: PDIC + * File Created: Wednesday, 12th May 2021 3:23:04 pm + * Author: kbarre (kevin.barre@epitech.eu) + * ----- + * Last Modified: Thursday, 22nd July 2021 12:58:00 am + * Modified By: kbarre (kevin.barre@epitech.eu>) + * ----- + * Licenses: EUPL + * ----- + * Copyright 2021 - 2021 neudinger + */ + +#include "PDITools.hpp" + +namespace PDI +{ + static inline bool BothAreSpaces(char lhs, char rhs) { return (lhs == rhs) && (lhs == ' '); } + // trim from start (in place) + static inline std::string <rim(std::string &str) + { + str.erase(str.begin(), + std::find_if( + str.begin(), str.end(), + [](const uint8_t &ch) -> bool + { + return !std::isspace(ch); + })); + return str; + } + + // trim from end (in place) + static inline std::string &rtrim(std::string &str) + { + str.erase(std::find_if( + str.rbegin(), str.rend(), + [](const uint8_t &ch) -> bool + { + return !std::isspace(ch); + }) + .base(), + str.end()); + return str; + } + + // trim from both ends (in place) + static inline std::string &trim(std::string &str) + { + return rtrim(ltrim(str)); + } + + std::string &clean(std::string &str) + { + trim(str); + std::string::iterator new_end(std::unique(str.begin(), str.end(), BothAreSpaces)); + str.erase(new_end, str.end()); + return str; + } + + void split(const std::string &str, + std::vector &out, + const char &delim) + { + size_t start; + size_t end = 0; + while ((start = str.find_first_not_of(delim, end)) != std::string::npos) + { + end = str.find(delim, start); + out.push_back(str.substr(start, end - start)); + } + } + + uint8_t charLen(const std::string &declstring, const char &elem) + { + if (declstring.find(elem) != std::string::npos) + return (declstring.find_last_of(elem) - declstring.find_first_of(elem) + 1); + return 0; + } + + // Extract all values between [] and [][]...[] and add to container + void extractBetweens(const std::string &str, + std::vector &container, + const std::string delims) + { + assert(delims.length() == 2 ? true : false); + std::size_t side_left, side_right = 0; + while ((side_left = str.find(delims[0], side_right)) != std::string::npos) + { + side_right = str.find(delims[1], side_left); + container.push_back(str.substr(side_left + 1, (side_right - (side_left + 1)))); + } + } + + void describePointer(const TypeAttr &typeIdentifier, + uint8_t &starsNbr, + const std::string &typeRepr, + std::ostringstream &ostream) + { + ostream << " { type: pointer, subtype: "; + if (--starsNbr) + describePointer(typeIdentifier, starsNbr, typeRepr, ostream); + else + ostream << typeRepr; + ostream << " }"; + } + + void describePointer(const TypeAttr &typeIdentifier, + std::string &typeRepr) + { + uint8_t starsNbr; + std::ostringstream ostream; + if (__builtin_sub_overflow(typeIdentifier.starsNbr, typeIdentifier.pragma.arraySizes.size(), &starsNbr)) + { + llvm::errs() << "cannot describe Pointer of" + << typeIdentifier.type + << " " << typeIdentifier.name << " \n"; + return; + } + ostream << " { type: pointer, subtype: "; + if (--starsNbr) + describePointer(typeIdentifier, starsNbr, typeRepr, ostream); + else + ostream << typeRepr; + if (typeIdentifier.offset) + ostream << ", offset: " << typeIdentifier.offset; + ostream << " }"; + typeRepr = ostream.str(); + } + + void describeArray(const std::vector &arrays, + std::string &typeRepr, + const uint64_t offset) + { + std::ostringstream ostream; + const std::size_t arrayLengh = arrays.size(); + + ostream << " { type: array, subtype: " + << typeRepr + << ", size: " << (arrayLengh > 1 ? "[" : ""); + + std::copy(arrays.cbegin(), arrays.cend() - 1, + std::ostream_iterator(ostream, ", ")); + ostream << arrays.back() << (arrayLengh > 1 ? "]" : ""); + if (offset) + ostream << ", offset: " << offset; + ostream << " }"; + + typeRepr = ostream.str(); + } + + void describePragmaArray(const TypeAttr &typeIdentifier, + std::string &typeRepr) + { + describeArray(typeIdentifier.pragma.arraySizes, typeRepr, typeIdentifier.offset); + } + + std::map + stringToMap(const std::string &str) + { + std::map pragmaParamsMap; + std::vector pragmaParamsList; + std::vector pragmaParamsVect; + split(str, pragmaParamsList, ';'); + for (const auto &pragma : pragmaParamsList) + { + split(pragma, pragmaParamsVect, ':'); + if (pragmaParamsVect.size() == 2) + pragmaParamsMap[pragmaParamsVect[0]] = pragmaParamsVect[1]; + pragmaParamsVect.clear(); + } + return pragmaParamsMap; + } + + void PragmaIdentifiers::load(const std::string &attributes) + { + auto annotAttr = stringToMap(attributes); + this->type = wrapMap(annotAttr).get(/* key= */ "type", + /* default_val= */ this->type); + extractBetweens(annotAttr["size"], /* &container */ this->arraySizes); + } + + const bool Switch::isRaised(const std::ostringstream &stringStream, + const clang::SourceLocation &sourceLocation) + { + if (stringStream.str() == "\"on") + { + this->isOffRaised = false; + this->isOnRaised = true; + this->pdiOnLoc = sourceLocation; + } + else if (stringStream.str() == "\"off") + { + this->isOffRaised = true; + this->pdiOffLoc = sourceLocation; + } + else + return false; + return true; + } + +} + +const std::string get_file_contents(const char *filename) +{ + std::string contents; + std::FILE *fp = std::fopen(filename, "r"); + if (fp) + { + std::fseek(fp, SEEK_SET, SEEK_END); + contents.resize(std::ftell(fp)); + std::rewind(fp); + std::fread(&contents[0], 1, contents.size(), fp); + std::fclose(fp); + } + return (contents); +} \ No newline at end of file diff --git a/src/pdic/PDITools.hpp b/src/pdic/PDITools.hpp new file mode 100644 index 0000000..fc34700 --- /dev/null +++ b/src/pdic/PDITools.hpp @@ -0,0 +1,162 @@ +/* + * File: PDITools.hpp + * Project: PDIC + * File Created: Wednesday, 12th May 2021 3:23:04 pm + * Author: kbarre (kevin.barre@epitech.eu) + * ----- + * Last Modified: Thursday, 22nd July 2021 12:58:12 am + * Modified By: kbarre (kevin.barre@epitech.eu>) + * ----- + * Licenses: EUPL + * ----- + * Copyright 2021 - 2021 neudinger + */ + +#if !defined(PDI_TOOLS) +#define PDI_TOOLS + +#include +#include +#include +#include + +#include // stringstream +#include // ofstream + +// Import clang::TypeInfo +#include "clang/AST/ASTContext.h" + +/// msg must be static const char[N] aka llvm::StringRef - Represent a constant reference to a string, i.e. a character +/// array and a length, which need not be null terminated. +#define RAISE(msg, node) ( \ + { \ + sourceManager.getDiagnostics().Report(node->getLocation(), \ + sourceManager.getDiagnostics().getCustomDiagID( \ + clang::DiagnosticsEngine::Error, \ + msg)); \ + PragmaError = true; \ + }) + +#define WARN(msg, node) ( \ + { \ + sourceManager.getDiagnostics().Report(node->getLocation(), \ + sourceManager.getDiagnostics().getCustomDiagID( \ + clang::DiagnosticsEngine::Warning, \ + msg)); \ + }) + +// Number of Bits in one byte +#define BYTE_SIZE 8 + +namespace PDI +{ + static char const attrAnnotate[] = "annotate"; + typedef struct Pragma_Identifiers + { + std::vector arraySizes; // values of arrays length ex [32,45] + std::string type; // variable type ex: int + const bool isEmpty() const { return (arraySizes.empty() && type.empty()); }; + const bool isNotEmpty() const { return !(arraySizes.empty() && type.empty()); }; + void load(const std::string &attributes); + } PragmaIdentifiers; + typedef struct Type_Attr + { + PragmaIdentifiers pragma; + std::string name; // variable name ex: foo + std::string type; // variable type ex: int + uintptr_t structId = 0; + uint8_t starsNbr = 0; // number of pointer stars ex 2 if ** + clang::SourceLocation sourceLoc; // Decl location in c/c++ file + std::vector arraySizes; // values of arrays length ex [32,45] + clang::CharUnits::QuantityType buffersize = 0; // size of current value in bytes + + // Struct Field attributes + uint64_t offset = 0; + + Type_Attr() = default; + Type_Attr(std::string typeName, + std::string typeType, + clang::SourceLocation sourceLocation) : name(typeName), + type(typeType), + sourceLoc(sourceLocation){}; + } TypeAttr; + typedef struct _Switch + { + bool isOnRaised = false; + clang::SourceLocation pdiOnLoc; // On location in c/c++ file + bool isOffRaised = false; + clang::SourceLocation pdiOffLoc; // Off location in c/c++ file + const bool isRaised(const std::ostringstream &StringStream, const clang::SourceLocation &Loc); + } Switch; + + using TypeMap = std::map; + + // Struct_Attr + typedef struct Struct_Attr + { + std::string name; // variable name ex: foo + clang::SourceLocation sourceLoc; // Decl location in c/c++ file + clang::CharUnits size; + std::vector alias; + TypeMap fields; + // if __attribute__((packed)) or #pragma pack() has been used + bool packed = false; + unsigned fieldSize = 0; + + Struct_Attr() = default; + Struct_Attr(std::string name, + clang::CharUnits size, + unsigned fieldCount, + clang::SourceLocation sourceLocation) : name(name), + sourceLoc(sourceLocation), + size(size), + fieldSize(fieldCount){}; + } StructAttr; + + using StructMap = std::map; + + template + struct mapWrapper + { + typedef typename MAP::key_type K; + typedef typename MAP::mapped_type V; + typedef typename MAP::const_iterator CIT; + + mapWrapper(const MAP &m) : m_map(m) {} + + const V &get(const K &key, const V &default_val) const + { + CIT it = m_map.find(key); + if (it == m_map.end()) + return default_val; + return it->second; + } + constexpr bool has(const K &key) + { + return !(m_map.find(key) == m_map.end()); + } + constexpr bool hasNot(const K &key) + { + return (m_map.find(key) == m_map.end()); + } + + private: + const MAP &m_map; + }; + + template + mapWrapper wrapMap(const MAP &m) + { + return mapWrapper(m); + } + + std::string &clean(std::string &str); + void split(const std::string &str, std::vector &cont, const char &delim = ' '); + uint8_t charLen(const std::string &declstring, const char &elem); + void describeArray(const std::vector &arrays, std::string &typeRepr, const uint64_t offset = 0); + void extractBetweens(const std::string &str, std::vector &container, const std::string delims = "[]"); + void describePointer(const TypeAttr &typeIdentifier, std::string &typeRepr); + void describePragmaArray(const TypeAttr &typeIdentifier, std::string &typeRepr); +} +const std::string get_file_contents(const char *filename); +#endif // PDI_TOOLS diff --git a/src/pdic/__init__.py b/src/pdic/__init__.py new file mode 100644 index 0000000..b9883fb --- /dev/null +++ b/src/pdic/__init__.py @@ -0,0 +1,61 @@ +__author__ = "Barre Kevin" +__maintainer__ = "Barre kevin" +__credits__ = ["https://kevinbarre.fr/"] +__email__ = "kevin.barre@epitech.eu" + +# -*- coding: utf-8 -*- +""" + +## PDIC + +PDIC: [(IDL)](https://en.wikipedia.org/wiki/Interface_description_language) Transpiler Source-to-source compiler C/C++ to YAML Portable Data Interface Description + +Example: + +example.py +```python +import os +import pathlib +import yaml +import pdic + +code_source: str = ''' +short notsee; +#pragma pdi on +int global_int; +char global_char; +void *global_pointer; +float **global_float_pointer_of_pointer; +int array[24]; +// pointer to array of int of size 42 +#pragma pdi type:int32; size:[42] +int **pointer_of_array; +double *array_of_pointers[21]; +#pragma pdi type:uint64 +#pragma pdi size:[10][10][10] +unsigned ****my_cube; +#pragma pdi off +unsigned short notsee_either; +''' + +if __name__ == '__main__': + here = pathlib.Path(__file__).parent.resolve() + pdi_yml_description_from_file: dict = yaml.load(pdic.file_to_pdi("examples/level_1.c"), Loader=yaml.FullLoader) + pdi_yml_description_from_txt: dict = yaml.load(pdic.str_to_pdi(code_source), Loader=yaml.FullLoader) + assert(pdi_yml_description_from_file == pdi_yml_description_from_txt) + print(pdi_yml_description_from_txt) +``` +""" + +from .pypdic import file_to_pdi, str_to_pdi, files_to_pdi + +try: + from .__version__ import __version__ # noqa: F401, F40 +except Exception: + pass + +__all__ = [ + "file_to_pdi", + "str_to_pdi", + "files_to_pdi" +] \ No newline at end of file diff --git a/src/pdic/main.hpp b/src/pdic/main.hpp new file mode 100644 index 0000000..a01c01d --- /dev/null +++ b/src/pdic/main.hpp @@ -0,0 +1,50 @@ +/* + * File: main.hpp + * Project: pdic + * File Created: Wednesday, 4th August 2021 9:43:26 pm + * Author: kbarre (kevin.barre@epitech.eu) + * ----- + * Last Modified: Wednesday, 9th August 2021 9:46:03 pm + * Modified By: kbarre (kevin.barre@epitech.eu>) + * ----- + * Licenses: EUPL + * ----- + * Copyright 2021 - 2021 neudinger + */ + +#if !defined(MAIN) +#define MAIN +#include "clang/Tooling/CommonOptionsParser.h" + +static llvm::cl::OptionCategory myToolCategory("pdi options"); +// CommonOptionsParser declares HelpMessage with a description of the common +// command-line options related to the compilation database and input files. +// It's nice to have this help message in all tools. +static llvm::cl::extrahelp CommonHelp(clang::tooling::CommonOptionsParser::HelpMessage); +static llvm::cl::opt outputFile("outputfilename"); +static llvm::cl::opt printout("stdout"); +int main(int argc, const char **argv) +{ + std::ostringstream ostream; + ostream << std::boolalpha; + + printout.setInitialValue("on"); + // Call ClangTool + auto expectedParser = clang::tooling::CommonOptionsParser(argc, argv, myToolCategory); + clang::tooling::ClangTool tool(expectedParser.getCompilations(), + expectedParser.getSourcePathList()); + auto pdiPluging = tool.run(clang::tooling::newFrontendActionFactory().get()); + + if (!pdiPluging) + { + serializeIn(ostream); + if (printout.getValue() == "on") + llvm::outs() << ostream.str() << "\n"; + if (!outputFile.empty()) + writeYML(ostream, outputFile.getValue()); + } + PDI::typeIdentifiers.clear(); + PDI::structIdentifiers.clear(); + return pdiPluging; +} +#endif // !MAIN \ No newline at end of file diff --git a/src/pdic/maintest.hpp b/src/pdic/maintest.hpp new file mode 100644 index 0000000..546e50c --- /dev/null +++ b/src/pdic/maintest.hpp @@ -0,0 +1,49 @@ +/* + * File: fakemain.hpp + * Project: pdic + * File Created: Friday, 13th August 2021 12:12:51 pm + * Author: kbarre (kevin.barre@epitech.eu) + * ----- + * Last Modified: Friday, 13th August 2021 12:13:04 pm + * Modified By: kbarre (kevin.barre@epitech.eu>) + * ----- + * Licenses: EUPL + * ----- + * Copyright 2021 - 2021 neudinger + */ + +#if !defined(MAIN_TEST) +#define MAIN_TEST + +#include +#include +#include + +const std::string runToolOn(const std::string &filepath) +{ + std::ostringstream ostream; + ostream << std::boolalpha; + + clang::tooling::runToolOnCode(std::make_unique(), + get_file_contents(filepath.c_str())); + serializeIn(ostream); + PDI::typeIdentifiers.clear(); + PDI::structIdentifiers.clear(); + return ostream.str(); +} + +const std::string mainTest(const std::vector filepaths) +{ + std::ostringstream ostream; + ostream << std::boolalpha; + + // serialize only runToolOnCode was successfull + for (const auto &item : filepaths) + clang::tooling::runToolOnCode(std::make_unique(), + get_file_contents(item.c_str())); + serializeIn(ostream); + PDI::typeIdentifiers.clear(); + PDI::structIdentifiers.clear(); + return ostream.str(); +} +#endif // !MAIN_TEST \ No newline at end of file diff --git a/src/pdic/pypdicbind.hpp b/src/pdic/pypdicbind.hpp new file mode 100644 index 0000000..cda4dbd --- /dev/null +++ b/src/pdic/pypdicbind.hpp @@ -0,0 +1,119 @@ +/* + * File: pypdicbind.hpp + * Project: pdic + * File Created: Wednesday, 4th August 2021 9:38:33 pm + * Author: kbarre (kevin.barre@epitech.eu) + * ----- + * Last Modified: Monday, 9th August 2021 3:25:37 pm + * Modified By: kbarre (kevin.barre@epitech.eu>) + * ----- + * Licenses: EUPL + * ----- + * Copyright 2021 - 2021 neudinger + */ + +#if !defined(PY_PDIC_BIND) +#define PY_PDIC_BIND +// pip3 install --use-feature=in-tree-build . -v + +#include +#include + +#include + +namespace python = pybind11; + +const std::string getPDIRepFromStr(const char *str) +{ + std::ostringstream ostream; + ostream << std::boolalpha; + + // serialize only runToolOnCode was successfull + if (clang::tooling::runToolOnCode(std::make_unique(), str)) + serializeIn(ostream); +#ifdef DEBUG + llvm::outs() << ostream.str() << "\n"; // <- Usefull for debug +#endif + PDI::typeIdentifiers.clear(); + PDI::structIdentifiers.clear(); + return ostream.str(); +} + +const std::string getPDIRepFromFile(const char *filepath) +{ + std::ostringstream ostream; + ostream << std::boolalpha; + + // serialize only runToolOnCode was successfull + if (clang::tooling::runToolOnCode(std::make_unique(), get_file_contents(filepath))) + serializeIn(ostream); +#ifdef DEBUG + llvm::outs() << ostream.str() << "\n"; // <- Usefull for debug +#endif + PDI::typeIdentifiers.clear(); + PDI::structIdentifiers.clear(); + return ostream.str(); +} + +const std::string getPDIRepFromFiles(const python::list filepaths) +{ + std::ostringstream ostream; + ostream << std::boolalpha; + // serialize only runToolOnCode was successfull + for (const auto &item : filepaths) + clang::tooling::runToolOnCode(std::make_unique(), + get_file_contents(item.cast().c_str())); + serializeIn(ostream); + PDI::typeIdentifiers.clear(); + PDI::structIdentifiers.clear(); + return ostream.str(); +} + +using namespace pybind11::literals; + +PYBIND11_MODULE(pypdic, m) +{ + m.doc() = "pypdic From C/C++ to Yaml Description C++ pybind API"; + m.def("file_to_pdi", &getPDIRepFromFile, + "file_to_pdi\n\ + Return yaml in (str)\n\ +\n\ + Parameters\n\ + ----------\n\ + filepath : str\n\ + Path of source code\n\ +\n\ + Returns\n\ + -------\n\ + str\n\ + Return pdi yaml representation of one file", "filepath"_a); + m.def("str_to_pdi", &getPDIRepFromStr, + "file_to_pdi\n\ + Return yaml in (str)\n\ +\n\ + Parameters\n\ + ----------\n\ + filepath : str\n\ + Source code\n\ +\n\ + Returns\n\ + -------\n\ + str\n\ + Return pdi yaml representation", + "source_code_in_str"_a); + m.def("files_to_pdi", &getPDIRepFromFiles, + "files_to_pdi\n\ + Return yaml in (str)\n\ +\n\ + Parameters\n\ + ----------\n\ + filepath : List[str]\n\ + Path of source code\n\ +\n\ + Returns\n\ + -------\n\ + str\n\ + Return pdi representation of all files in one yaml", + "source_code_in_list"_a); +} +#endif // !PY_PDIC_BIND \ No newline at end of file diff --git a/tests/c_yaml_test.cc b/tests/c_yaml_test.cc new file mode 100644 index 0000000..604a96c --- /dev/null +++ b/tests/c_yaml_test.cc @@ -0,0 +1,12 @@ +#include + +#include "PDITools.hpp" + +const std::string runToolOn(const std::string &filepath); +// Compare c files to expected yaml +TEST(TestLevel1, CFiles) +{ + auto cfile = runToolOn("../examples/level_1.c"); + auto yamlfile = get_file_contents("../examples/expected/level_1.yml"); + EXPECT_STREQ(cfile.c_str(), yamlfile.c_str()); +} \ No newline at end of file diff --git a/tests/c_yaml_test.py b/tests/c_yaml_test.py new file mode 100644 index 0000000..3388cc1 --- /dev/null +++ b/tests/c_yaml_test.py @@ -0,0 +1,15 @@ +import unittest +import yaml +import pdic + +class LevelTest(unittest.TestCase): + def test_level1(self): + pdi_yml_description: dict = yaml.load(pdic.files_to_pdi(["examples/level_1.c"]), Loader=yaml.FullLoader) + with open("examples/expected/level_1.yml") as file: + pdi_yml_description_expected: dict = yaml.load(file.read(), Loader=yaml.FullLoader) + file.close() + self.assertEqual(pdi_yml_description, pdi_yml_description_expected) + + +if __name__ == '__main__': + unittest.main()