diff --git a/.coveragerc b/.coveragerc new file mode 100644 index 0000000000..c05422050e --- /dev/null +++ b/.coveragerc @@ -0,0 +1,5 @@ +[run] +plugins = Cython.Coverage +branch = True +source = dpctl +omit = dpctl/tests/*, *__init__.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 5479c3109d..69038c44d4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,10 +6,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] - ### Added - Documentation improvements -- Cmake improvements and Coverage for C API +- Cmake improvements and Coverage for C API, Cython and Python - Add support for Level Zero - Code of conduct @@ -17,7 +16,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Remove `cython` from `install_requires`. It allows use `dpCtl` in `numba` extensions. - Incorrect import in example. - ## [0.5.0] - 2020-12-17 ### Added - `_Memory.get_pointer_type` static method which returns kind of USM pointer. @@ -67,7 +65,6 @@ supports USM. - Fix Gtests configuration. ## [0.3.7] - 2020-10-08 - ### Fixed - A crash on Windows due a Level Zero driver problem. Each device was getting enumerated twice. To handle the issue, we added a temporary fix to use only first device for each device type and backend (#118). diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e0de9adcc5..a0de847880 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,6 +1,6 @@ -# Mechanical Source Issues¶ +# Mechanical Source Issues -## Source Code Formatting¶ +## Source Code Formatting ### C/C++ code style @@ -87,3 +87,62 @@ Every Python and Cython file should only include the following license header: # limitations under the License. ``` The copyright year should be updated every calendar year. + +## Code Coverage + +Implement python, cython and c++ file coverage using `coverage` and `llvm-cov` packages on Linux. + +### Using Code Coverage + +You need to install additional packages and add an environment variable to cover: +- conda install cmake +- conda install coverage +- conda install conda-forge::lcov +- conda install conda-forge::gtest +- export CODE_COVERAGE=ON + +CODE_COVERAGE allows you to build a debug version of dpctl and enable string tracing, which allows you to analyze strings to create a coverage report. +It was added for the convenience of configuring the CI in the future. + +Installing the dpctl package: +- python setup.py develop + +It is important that there are no files of the old build in the folder. +Use `git clean -xdf` to clean up the working tree. + +The coverage report will appear during the build in the console. This report contains information about c++ file coverage. +The `dpctl-c-api-coverage` folder will appear in the root folder after installation. +The folder contains a report on the coverage of c++ files in html format. + +You need to run tests to cover the cython and python files: +- coverage run -m unittest dpctl.tests + +The required flags for the command coverage run are contained in the file `.coveragerc`. + +The simplest reporting is a textual summary produced with report: +- coverage report + +For each module executed, the report shows the count of executable statements, the number of those statements missed, and the resulting coverage, expressed as a percentage. + +The `-m` flag also shows the line numbers of missing statements: +- coverage report -m + +To create an annotated HTML listings with coverage results: +- coverage html + +The `htmlcov` folder will appear in the root folder of the project. It contains reports on the coverage of python and cython files in html format. + +Erase previously collected coverage data: +- coverage erase + +### Error in the build process + +An error occurs during the dcptl build with the CODE_COVERAGE environment variable: +``` +error: '../compat/unistd.h' file not found, did you mean 'compat/unistd.h'? +# include "../compat/unistd.h" + ^ +1 error generated. +``` +The error is related to the `tcl` package. +You need to remove the tcl package to resolve this error. diff --git a/scripts/build_backend.py b/scripts/build_backend.py index 890dacb691..a30f03dbff 100644 --- a/scripts/build_backend.py +++ b/scripts/build_backend.py @@ -35,6 +35,7 @@ assert False, sys.platform + " not supported" ONEAPI_ROOT = os.environ.get("ONEAPI_ROOT") +CODE_COVERAGE = os.environ.get("CODE_COVERAGE") if IS_LIN: DPCPP_ROOT = os.path.join(ONEAPI_ROOT, "compiler/latest/linux") @@ -55,20 +56,41 @@ backends = os.path.join(dpctl_dir, "dpctl-capi") if IS_LIN: - cmake_args = [ - "cmake", - "-DCMAKE_BUILD_TYPE=Release", - "-DCMAKE_INSTALL_PREFIX=" + INSTALL_PREFIX, - "-DCMAKE_PREFIX_PATH=" + INSTALL_PREFIX, - "-DDPCPP_INSTALL_DIR=" + DPCPP_ROOT, - "-DCMAKE_C_COMPILER:PATH=" + os.path.join(DPCPP_ROOT, "bin", "clang"), - "-DCMAKE_CXX_COMPILER:PATH=" + os.path.join(DPCPP_ROOT, "bin", "dpcpp"), - "-DDPCTL_ENABLE_LO_PROGRAM_CREATION=ON", - backends, - ] - subprocess.check_call(cmake_args, stderr=subprocess.STDOUT, shell=False) - subprocess.check_call(["make", "V=1", "-j", "4"]) - subprocess.check_call(["make", "install"]) + if CODE_COVERAGE: + cmake_args = [ + "cmake", + "-DCMAKE_BUILD_TYPE=Debug", + "-DCMAKE_INSTALL_PREFIX=" + INSTALL_PREFIX, + "-DCMAKE_PREFIX_PATH=" + INSTALL_PREFIX, + "-DDPCPP_INSTALL_DIR=" + DPCPP_ROOT, + "-DCMAKE_C_COMPILER:PATH=" + os.path.join(DPCPP_ROOT, "bin", "clang"), + "-DCMAKE_CXX_COMPILER:PATH=" + os.path.join(DPCPP_ROOT, "bin", "dpcpp"), + "-DDPCTL_ENABLE_LO_PROGRAM_CREATION=ON", + "-DDPCTL_BUILD_CAPI_TESTS=ON", + "-DDPCTL_GENERATE_COVERAGE=ON", + "-DDPCTL_COVERAGE_REPORT_OUTPUT_DIR=" + dpctl_dir, + backends, + ] + subprocess.check_call(cmake_args, stderr=subprocess.STDOUT, shell=False) + subprocess.check_call(["make", "V=1", "-j", "4"]) + subprocess.check_call(["make", "install"]) + subprocess.check_call(["make", "lcov-genhtml"]) + + else: + cmake_args = [ + "cmake", + "-DCMAKE_BUILD_TYPE=Release", + "-DCMAKE_INSTALL_PREFIX=" + INSTALL_PREFIX, + "-DCMAKE_PREFIX_PATH=" + INSTALL_PREFIX, + "-DDPCPP_INSTALL_DIR=" + DPCPP_ROOT, + "-DCMAKE_C_COMPILER:PATH=" + os.path.join(DPCPP_ROOT, "bin", "clang"), + "-DCMAKE_CXX_COMPILER:PATH=" + os.path.join(DPCPP_ROOT, "bin", "dpcpp"), + "-DDPCTL_ENABLE_LO_PROGRAM_CREATION=ON", + backends, + ] + subprocess.check_call(cmake_args, stderr=subprocess.STDOUT, shell=False) + subprocess.check_call(["make", "V=1", "-j", "4"]) + subprocess.check_call(["make", "install"]) os.chdir(dpctl_dir) for file in glob.glob(os.path.join(dpctl_dir, "install", "lib", "*.so")): diff --git a/setup.py b/setup.py index b476eb29de..e2b13d9cae 100644 --- a/setup.py +++ b/setup.py @@ -113,6 +113,7 @@ def extensions(): ela = get_sdl_ldflags() libs = [] librarys = [] + CODE_COVERAGE = os.environ.get("CODE_COVERAGE") if IS_LIN: libs += ["rt", "DPCTLSyclInterface"] @@ -148,6 +149,15 @@ def extensions(): "language": "c++", } + if CODE_COVERAGE: + extension_args.update( + { + "define_macros": [ + ("CYTHON_TRACE", "1"), + ] + } + ) + extensions = [ Extension( "dpctl._sycl_context", @@ -199,8 +209,10 @@ def extensions(): **extension_args ), ] - - exts = cythonize(extensions) + if CODE_COVERAGE: + exts = cythonize(extensions, compiler_directives={"linetrace": True}) + else: + exts = cythonize(extensions) return exts