├── .coveragerc ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── feature_request.md │ └── other_issue.md ├── PULL_REQUEST_TEMPLATE.md └── workflows │ └── build_x86_64.yml ├── .gitignore ├── Build.ps1 ├── CONTRIBUTING.md ├── Dockerfile ├── Dockerfile-package ├── Get-Binary-Name.ps1 ├── Integration-Test.ps1 ├── LICENSE ├── MANIFEST.in ├── NOTICE ├── README.md ├── Setup-Environment.ps1 ├── THIRD-PARTY-LICENSES.txt ├── Test.ps1 ├── Unit-Test.ps1 ├── build.sh ├── container-test.sh ├── doc ├── golang.md ├── java.md └── python.md ├── getBinaryName.sh ├── integration-test.sh ├── load_test.sh ├── po └── en │ └── LC_MESSAGES │ └── advisor.po ├── porting-advisor-win-x64.spec ├── requirements-build.txt ├── requirements.txt ├── sample-projects ├── dotnet-samples │ └── sample.csproj ├── go-samples │ ├── compatible │ │ ├── go.mod │ │ └── main.go │ └── incompatible │ │ ├── go.mod │ │ └── main.go ├── java-samples │ ├── main.java │ ├── pom.xml │ └── submain.java ├── node-samples │ └── package.json ├── python-samples │ ├── compatible │ │ └── requirements.txt │ ├── incompatible │ │ └── requirements.txt │ ├── main.py │ ├── sample_report.html │ └── submain.py └── ruby-samples │ └── Gemfile ├── setup-environment.sh ├── src ├── advisor │ ├── __init__.py │ ├── constants │ │ ├── __init__.py │ │ ├── arch_specific_libs.py │ │ ├── arch_specific_options.py │ │ ├── arch_strings.py │ │ └── intrinsics.py │ ├── filters │ │ ├── __init__.py │ │ ├── issue_type_filter.py │ │ ├── other_issues_filter.py │ │ ├── port_filter.py │ │ └── target_os_filter.py │ ├── helpers │ │ ├── c │ │ │ └── naive_cpp.py │ │ ├── find_port.py │ │ ├── java │ │ │ └── java_tool_invoker.py │ │ ├── python │ │ │ └── python_version_checker.py │ │ ├── rules_loader.py │ │ ├── utils.py │ │ └── version_comparer.py │ ├── main.py │ ├── manifester │ │ ├── __init__.py │ │ ├── dependency.py │ │ ├── go_manifester.py │ │ ├── manifester.py │ │ ├── manifester_factory.py │ │ ├── maven_manifester.py │ │ ├── npm_manifester.py │ │ ├── nuget_manifester.py │ │ ├── pip_manifester.py │ │ ├── regex_manifester.py │ │ └── ruby_manifester.py │ ├── parsers │ │ ├── __init__.py │ │ ├── comment_parser.py │ │ ├── continuation_parser.py │ │ ├── naive_comment_parser.py │ │ ├── naive_function_parser.py │ │ ├── python_comment_parser.py │ │ ├── python_requirements_parser.py │ │ └── ruby_gem_parser.py │ ├── reports │ │ ├── __init__.py │ │ ├── csv_issue_type_count_by_file_report.py │ │ ├── csv_report.py │ │ ├── dependencies_report.py │ │ ├── error.py │ │ ├── html_report.py │ │ ├── issues │ │ │ ├── __init__.py │ │ │ ├── arch_specific_build_option_issue.py │ │ │ ├── arch_specific_library_issue.py │ │ │ ├── asm_source_issue.py │ │ │ ├── build_command_issue.py │ │ │ ├── compiler_specific_issue.py │ │ │ ├── config_guess_issue.py │ │ │ ├── cross_compile_issue.py │ │ │ ├── define_other_arch_issue.py │ │ │ ├── dependency_version_issue.py │ │ │ ├── host_cpu_detection_issue.py │ │ │ ├── inline_asm_issue.py │ │ │ ├── intrinsic_issue.py │ │ │ ├── issue.py │ │ │ ├── issue_type_config.py │ │ │ ├── issue_types.py │ │ │ ├── native_methods_issue.py │ │ │ ├── no_equivalent_inline_asm_issue.py │ │ │ ├── no_equivalent_intrinsic_issue.py │ │ │ ├── no_equivalent_issue.py │ │ │ ├── old_crt_issue.py │ │ │ ├── other_issues.py │ │ │ ├── pragma_simd_issue.py │ │ │ ├── preprocessor_error_issue.py │ │ │ └── unsupported_dependency_issue.py │ │ ├── json_report.py │ │ ├── localization.py │ │ ├── remarks │ │ │ ├── __init__.py │ │ │ ├── config_guess_remark.py │ │ │ ├── dependency_version_remark.py │ │ │ ├── files_scanned_remark.py │ │ │ ├── language_version_remark.py │ │ │ ├── no_issues_found_remark.py │ │ │ ├── ported_inline_asm_remark.py │ │ │ ├── ported_source_files_remark.py │ │ │ ├── special_instructions_remark.py │ │ │ ├── tool_version_remark.py │ │ │ └── version_remark.py │ │ ├── report.py │ │ ├── report_factory.py │ │ ├── report_item.py │ │ ├── report_mp.py │ │ └── text_report.py │ ├── rules │ │ ├── csharp.json │ │ ├── go.json │ │ ├── java.json │ │ ├── node.json │ │ ├── python.json │ │ ├── ruby.json │ │ └── sample.json │ ├── scanners │ │ ├── __init__.py │ │ ├── asm_source_scanner.py │ │ ├── auto_scanner.py │ │ ├── cmake_scanner.py │ │ ├── config_guess_scanner.py │ │ ├── go_scanner.py │ │ ├── java_scanner.py │ │ ├── language_scanner.py │ │ ├── makefile_scanner.py │ │ ├── meson_scanner.py │ │ ├── python_scanner.py │ │ ├── scanner.py │ │ ├── scanners.py │ │ └── source_scanner.py │ ├── templates │ │ ├── status-info.svg │ │ ├── status-negative.svg │ │ ├── status-positive.svg │ │ ├── status-warning.svg │ │ └── template.html │ └── tools │ │ └── ampere-ready-java │ │ ├── .gitignore │ │ ├── LICENSE │ │ ├── README.md │ │ ├── pom.xml │ │ ├── src │ │ ├── main │ │ │ └── java │ │ │ │ └── com │ │ │ │ └── ampere │ │ │ │ └── labs │ │ │ │ └── GravitonReadyAssessor │ │ │ │ ├── ClassInfo.java │ │ │ │ ├── Command.java │ │ │ │ ├── Config.java │ │ │ │ ├── JarChecker.java │ │ │ │ ├── JarCheckerInterface.java │ │ │ │ ├── JarFileScanner.java │ │ │ │ ├── JarManifestScanner.java │ │ │ │ ├── JarNativeInfo.java │ │ │ │ ├── NativeCodeManifest.java │ │ │ │ ├── NativeCodeManifestRecord.java │ │ │ │ └── SimpleLogger.java │ │ └── test │ │ │ ├── files │ │ │ └── config.json │ │ │ └── java │ │ │ └── com │ │ │ └── ampere │ │ │ └── labs │ │ │ └── GravitonReadyAssessor │ │ │ ├── AppTest.java │ │ │ └── ConfigFileTest.java │ │ └── target │ │ └── placeholder.md ├── porting-advisor.py └── updater.py ├── test-helpers.sh ├── test.sh ├── tests-baseline ├── directory_not_found_test.txt ├── missing_arguments_test.txt └── sample_applications_test.json ├── unit-test.sh └── unittest ├── test_asm_source_scanner.py ├── test_cmake_scanner.py ├── test_config_guess_scanner.py ├── test_continuation_parser.py ├── test_csv_issue_type_count_by_file_report.py ├── test_csv_report.py ├── test_find_port.py ├── test_go_scanner.py ├── test_issue_type_config.py ├── test_issue_type_filter.py ├── test_item_type.py ├── test_java_scanner.py ├── test_java_tool_invoker.py ├── test_json_report.py ├── test_makefile_scanner.py ├── test_manifester.py ├── test_meson_scanner.py ├── test_naive_comment_parser.py ├── test_naive_cpp.py ├── test_naive_function_parser.py ├── test_port_filter.py ├── test_python_comment_parser.py ├── test_python_requirements_parser.py ├── test_python_scanner.py ├── test_python_version_checker.py ├── test_report_factory.py ├── test_ruby_gem_parser.py ├── test_rules_loader.py ├── test_source_scanner.py ├── test_target_os_filter.py └── test_version_comparer.py /.coveragerc: -------------------------------------------------------------------------------- 1 | [run] 2 | omit = 3 | */__init__.py 4 | src/advisor/reports/dependencies_report.py 5 | src/advisor/reports/html_report.py 6 | src/advisor/scanners/auto_scanner.py 7 | src/advisor/scanners/scanner.py 8 | src/advisor/scanners/scanners.py 9 | src/advisor/main.py 10 | src/porting-advisor.py -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | ## Describe the bug 11 | A clear and concise description of what the bug is. 12 | 13 | ### Expected behavior 14 | A clear and concise description of what you expected to happen. 15 | 16 | ### Steps to Reproduce 17 | Steps to reproduce the behavior: 18 | 1. Run with parameters '...' 19 | 2. Open report... 20 | 3. Scroll down to '...' 21 | 4. See error 22 | 23 | ### Screenshots 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | ### Desktop (please complete the following information) 27 | - OS: [e.g. Windows, macOS, Linux, etc] 28 | - Version/Distro: [e.g. 11, Big Sur, Ubuntu 18.04] 29 | - Processor Architecture: [e.g. x86/x64, ARM] 30 | - Python Version: 31 | - Java Version (if applicable): 32 | 33 | ### Additional context 34 | Add any other context about the problem here. -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | ## Description 11 | A clear and concise description of what the problem is. 12 | 13 | ## Solution idea(s) 14 | A clear and concise description of what you want to happen. 15 | 16 | ## Additional context 17 | Add any other context or screenshots about the feature request here. -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/other_issue.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Other issues 3 | about: Not a bug or feature request? Let us know how else we can improve. 4 | title: "[Other Issue]" 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | ## Description of issue 11 | Describe the issue 12 | 13 | ## Additional context 14 | Provide any additional information -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | #### Description of change 2 | [//]: # (What are you trying to fix? What did you change) 3 | 4 | #### Issue 5 | [//]: # (Having an issue # for the PR is required for tracking purposes. If an existing issue does not exist please create one.) 6 | 7 | #### PR reviewer notes 8 | [//]: # (Let us know if there is anything we should focus on.) 9 | 10 | 11 | By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice. -------------------------------------------------------------------------------- /.github/workflows/build_x86_64.yml: -------------------------------------------------------------------------------- 1 | name: Build x86_64 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build-x86_64: 7 | 8 | runs-on: ${{ matrix.os }} 9 | env: 10 | IS_WINDOWS: ${{ toJSON(matrix.os == 'windows-latest') }} 11 | strategy: 12 | fail-fast: false 13 | matrix: 14 | os: ["ubuntu-latest", "macos-latest", "windows-latest"] 15 | 16 | steps: 17 | - uses: actions/checkout@v3 18 | 19 | - name: Cache sample-applications 20 | uses: actions/cache@v3 21 | with: 22 | enableCrossOsArchive: False 23 | path: sample-applications 24 | key: sample-applications 25 | 26 | - name: Set up Python 3.10 27 | uses: actions/setup-python@v4 28 | with: 29 | python-version: "3.10" 30 | cache: 'pip' 31 | cache-dependency-path: | 32 | **/requirements*.txt 33 | 34 | - name: Set up Oracle JDK 17 35 | uses: actions/setup-java@v3 36 | with: 37 | java-version: '17' 38 | distribution: 'oracle' 39 | cache: 'maven' 40 | 41 | - name: Run build and tests on Linux/MacOS 42 | if: ${{ !fromJSON(env.IS_WINDOWS) }} 43 | run: | 44 | ./test.sh 45 | 46 | - name: Run build and tests on Windows 47 | if: ${{ fromJSON(env.IS_WINDOWS) }} 48 | run: | 49 | .\Test.ps1 50 | -------------------------------------------------------------------------------- /Build.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .Synopsis 3 | Converts the porting-advisor python module into a Windows executable. 4 | #> 5 | 6 | if (!(Test-Path -Path .venv)) { 7 | .\Setup-Environment.ps1 8 | } 9 | 10 | $Filename = .\Get-Binary-Name.ps1 11 | Write-Host "*** Will use $Filename as name ***" 12 | 13 | try { 14 | mvn package --file .\src\advisor\tools\ampere-ready-java\pom.xml 15 | } catch { 16 | Write-Host "Could not find Maven. Skipping jar generation for Ampere Ready Java tool." 17 | } 18 | 19 | Write-Host "🏗️ Generating executable" 20 | pyinstaller --clean porting-advisor-win-x64.spec --noconfirm 21 | if($LASTEXITCODE -ne 0) { 22 | throw "**ERROR**: pyinstaller failed, binary was not created" 23 | } 24 | 25 | Write-Output "🎉 *** Success: Executable saved at dist\$Filename ***" 26 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:22.04 as builder 2 | 3 | RUN apt update \ 4 | && apt install -y python3.11 python3.11-dev python3.11-venv python3-pip openjdk-11-jdk maven binutils \ 5 | && apt clean 6 | 7 | ENV MAVEN_HOME=/usr/share/maven 8 | 9 | COPY src src/ 10 | COPY build.sh setup-environment.sh getBinaryName.sh requirements-build.txt ./ 11 | SHELL ["/bin/bash", "-c"] 12 | RUN /usr/bin/python3.11 -m venv .venv \ 13 | && source .venv/bin/activate \ 14 | && python3 -m pip install -r requirements-build.txt \ 15 | && FILE_NAME=porting-advisor ./build.sh 16 | 17 | RUN mv dist/porting-advisor /opt/porting-advisor 18 | 19 | # Use Eclipse Temurin JRE 17 as runtime 20 | FROM eclipse-temurin:17-jre as runtime 21 | COPY --from=builder /opt/porting-advisor /usr/bin/porting-advisor 22 | ENTRYPOINT ["/usr/bin/porting-advisor"] 23 | -------------------------------------------------------------------------------- /Dockerfile-package: -------------------------------------------------------------------------------- 1 | FROM ubuntu:22.04 2 | COPY ./dist/porting-advisor /usr/bin/porting-advisor 3 | ENTRYPOINT ["/usr/bin/porting-advisor"] 4 | -------------------------------------------------------------------------------- /Get-Binary-Name.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .Synopsis 3 | Get the name for the binary to generate. 4 | #> 5 | $ProcessorSuffix = (Get-CimInstance Win32_OperatingSystem).OSArchitecture | Select-Object -first 1 6 | if ($ProcessorSuffix = '64-bit') { 7 | $ProcessorSuffix = 'x64' 8 | } else { 9 | $ProcessorSuffix = 'other' 10 | } 11 | Write-Output "porting-advisor-win-$ProcessorSuffix" -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include README.md 2 | include Apache-2.0.txt 3 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | Copyright Ampere Computing, Inc. or its affiliates. All Rights Reserved. 2 | -------------------------------------------------------------------------------- /Setup-Environment.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .Synopsis 3 | Sets up Python Virtual Environment. 4 | #> 5 | 6 | try { 7 | Get-Command python | ForEach-Object { 8 | $PYTHON_VERSION_MAJOR = [int]($_.FileVersionInfo.ProductVersion -split '\.')[0] 9 | $PYTHON_VERSION_MINOR = [int]($_.FileVersionInfo.ProductVersion -split '\.')[1] 10 | 11 | if ($PYTHON_VERSION_MAJOR -ge 3 -and $PYTHON_VERSION_MINOR -ge 10) { 12 | $PYTHON3 = $_.Name 13 | } 14 | } 15 | $p = & $PYTHON3 "--version" 16 | } catch { 17 | throw '**ERROR**: python3.10+ is missing, please install it before running this build script' 18 | } 19 | 20 | try { 21 | $p = pip --version 22 | } catch { 23 | throw '**ERROR**: pip is missing, please install it before running this build script' 24 | } 25 | 26 | if (!(Test-Path -Path .venv)) { 27 | Write-Host "💻 Creating Python virtual environment" 28 | & $PYTHON3 "-m" "venv" ".venv" 29 | if($LASTEXITCODE -ne 0) { 30 | throw "**ERROR**: could not create Python Virtual Environment." 31 | } 32 | } 33 | 34 | Write-Host "💡 Making sure Python Virtual Environment is active" 35 | .\.venv\Scripts\Activate.ps1 36 | if($LASTEXITCODE -ne 0) { 37 | throw "**ERROR**: could not activate Python Virtual Environment." 38 | } 39 | 40 | Write-Host "☁️ Installing requirements" 41 | pip install -r requirements-build.txt 42 | if($LASTEXITCODE -ne 0) { 43 | throw "**ERROR**: error installing required packages" 44 | } 45 | 46 | -------------------------------------------------------------------------------- /Test.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .Synopsis 3 | Builds project, runs unit tests, runs integration tests 4 | #> 5 | 6 | Write-Host "🐍 Setup virtual environment" 7 | .\Setup-Environment.ps1 8 | if($LASTEXITCODE -ne 0) { 9 | throw "*ERROR**: failed to initialize Python Virtual Environment" 10 | } 11 | 12 | Write-Host "🔬 Running unit tests" 13 | .\Unit-Test.ps1 14 | if($LASTEXITCODE -ne 0) { 15 | throw "*ERROR**: unit tests failed" 16 | } 17 | 18 | Write-Host "⚒️ Building project" 19 | .\Build.ps1 20 | if($LASTEXITCODE -ne 0) { 21 | throw "**ERROR**: failed to build project" 22 | } 23 | 24 | Write-Host "🧪 Running integration tests" 25 | .\Integration-Test.ps1 26 | if($LASTEXITCODE -ne 0) { 27 | throw "*ERROR**: integration tests failed" 28 | } 29 | -------------------------------------------------------------------------------- /Unit-Test.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .Synopsis 3 | Run unit tests for project. 4 | #> 5 | Write-Host "🔬 *** running unit tests ***" 6 | $Err = coverage run --source=./src -m unittest discover -s unittest -p "test_*.py" -v 7 | if ($null -ne $Err) { 8 | Write-Error "Unit tests failed" 9 | } 10 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # This script converts the porting-advisor python code into 4 | # a x86 or arm64 Linux or Mac binary like a.out. 5 | # As a prerequisite, pip3 must already be installed 6 | # The resulting native binary does not need sudo to execute. 7 | 8 | if [ ! -f ".venv/bin/activate" ]; then 9 | ./setup-environment.sh 10 | fi 11 | 12 | . .venv/bin/activate 13 | 14 | if [[ -z "${FILE_NAME}" ]]; then 15 | FILE_NAME=`./getBinaryName.sh` 16 | else 17 | FILE_NAME="${FILE_NAME}" 18 | fi 19 | 20 | echo "*** Will use $FILE_NAME as name ***" 21 | 22 | if hash mvn 23 | then 24 | mvn package --file ./src/advisor/tools/ampere-ready-java/pom.xml 25 | if [ $? -ne 0 ]; then 26 | echo "**ERROR**: error generating jar for Ampere Ready Java tool" && exit 1 27 | fi 28 | fi 29 | 30 | echo "🏗️ Generating executable" 31 | CERT_PATH=$(python3 -c 'import sysconfig; print(sysconfig.get_paths()["purelib"])') 32 | pyinstaller --onefile --clean --noconfirm --distpath dist --add-data 'src/advisor/rules/*.json:advisor/rules' --add-data 'src/advisor/tools/ampere-ready-java/target/*:advisor/tools/ampere-ready-java/target' --add-data 'src/advisor/templates/template.html:advisor/templates' --add-data "$CERT_PATH/certifi/cacert.pem:certifi" --name "$FILE_NAME" "src/porting-advisor.py" --runtime-hook 'src/updater.py' --exclude-module readline 33 | if [ $? -ne 0 ]; then 34 | echo "**ERROR**: pyinstaller failed, binary was not created" && exit 1 35 | fi 36 | echo 🎉 "*** Success: Executable saved at dist/$FILE_NAME ***" 37 | exit 0 38 | -------------------------------------------------------------------------------- /container-test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source ./test-helpers.sh 4 | 5 | # This script tests container image build and runs a couple of 6 | # tests against the generated image. 7 | 8 | docker build -t porting-advisor . 9 | if [ $? -ne 0 ]; then 10 | echo "**ERROR**: building container image" && exit 1 11 | fi 12 | 13 | echo "Running container on samples to console" 14 | docker run --rm -v $(pwd)/sample-projects:/source porting-advisor /source > console_test.txt 15 | test_report 'console' 'console_test.txt' "${lines_to_find[@]}" 16 | if [ $? -ne 0 ]; then 17 | echo "**ERROR**: running container to console" && exit 1 18 | fi 19 | rm console_test.txt 20 | 21 | echo "Running container on samples to HTML report" 22 | docker run --rm -v $(pwd):/source porting-advisor /source/sample-projects --output /source/test.html 23 | test_report 'html' 'test.html' "${lines_to_find[@]}" 24 | if [ $? -ne 0 ]; then 25 | echo "**ERROR**: running container to html report" && exit 1 26 | fi 27 | rm -f test.html 28 | 29 | exit 0 30 | -------------------------------------------------------------------------------- /doc/golang.md: -------------------------------------------------------------------------------- 1 | # Go on Ampere Processors 2 | 3 | Go is a statically typed, compiled high-level programming language designed at Google. It is often referred to as Golang because of its former domain name, golang.org, but its proper name is Go. Visit _[Wikipedia](https://en.wikipedia.org/wiki/Go_(programming_language))_ to get more info about Go. 4 | 5 | Go has added supported for ARM64 architecture since Go 1.5 (August 2015). Go was well-positioned for rasing momentum of ARM64 architecture in recent years. Go is well supported on Ampere Processor since it's ARM64 nature. visit _[Go on ARM and Beyond](https://go.dev/blog/ports)_ to get more info about Go's porting efforts on ARM architecture. 6 | 7 | Go for ARM64 can be downloaded from _[the Go download page](https://go.dev/dl/)_. 8 | 9 | ## Recent releases 10 | 11 | | VERSION | RELEASE DATE | CHANGES ON ARM64 | 12 | |:--------:|:------------:|:------------------:| 13 | | Go 1.20 | 2023-02-01 | crypto/rsa now uses a new, safer, constant-time backend. This causes a CPU runtime increase for decryption operations between approximately 15% (RSA-2048 on amd64) and 45% (RSA-4096 on arm64) | 14 | | Go 1.19 | 2022-08-02 | Support for debugger-injected function calls has been added on ARM64. The compiler now uses a jump table to implement large integer and string switch statements. Performance improvements for the switch statement vary but can be on the order of 20% faster | 15 | | Go 1.18 | 2022-03-15 | Enhance ARM64 support for Windows and iOS | 16 | 17 | ## Regression results 18 | 19 | Ampere running Daily regression tests on platforms powered by Ampere processors with Go official image on DockerHub to ensure each platform based on Ampere processors compatible with last changes from official Go release. Visit _[regression results page](https://amperecomputing.com/solution/go/regression-results)_ to find out regression status. 20 | -------------------------------------------------------------------------------- /getBinaryName.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # get binary name 4 | OS_SUFFIX="Other" 5 | case "$(uname -s)" in 6 | Darwin) 7 | OS_SUFFIX="macosx" 8 | ;; 9 | 10 | Linux) 11 | OS_SUFFIX="linux" 12 | ;; 13 | esac 14 | 15 | if [ $OS_SUFFIX = "Other" ] 16 | then 17 | echo "**ERROR**: Unsupported platform" && exit 1 18 | fi 19 | 20 | PROCESSOR_SUFFIX=`uname -m` 21 | FILE_NAME="porting-advisor-$OS_SUFFIX-$PROCESSOR_SUFFIX" 22 | echo $FILE_NAME -------------------------------------------------------------------------------- /load_test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | 4 | function check_integrity() { 5 | _prog_lang=$1 6 | _baseline_json=$2 7 | _app_html=$3 8 | _v_a=(`jq ".$1.integrity[]" $2`) 9 | for ((_idx=0;_idx<`jq ".$1.integrity | length" $2`;_idx++)); 10 | do 11 | _k=`jq -r ".$1.integrity | keys_unsorted" $2 | jq -r ".[$_idx]"` 12 | _res=`grep "$_k" "$_app_html" | wc -l` 13 | echo -e "-- [$_k] target: ${_v_a[_idx]}, found: $_res" 14 | if [ $_res -lt ${_v_a[_idx]} ]; then 15 | echo "**FAILED**: integrity check test" && exit 1 16 | fi 17 | done 18 | echo "**PASSED**: integrity check test" 19 | } 20 | 21 | function check_prompts() { 22 | _prog_lang=$1 23 | _baseline_json=$2 24 | _app_html=$3 25 | _v_a=(`jq -c ".$1.prompts[]" $2`) 26 | for ((_idx=0;_idx<`jq ".$1.prompts | length" $2`;_idx++)); 27 | do 28 | _prompt=`jq -r ".$1.prompts" $2 | jq -r ".[$_idx]"` 29 | if grep --quiet "$_prompt" "$_app_html" 30 | then 31 | echo -e "-- ["$_prompt"] found" 32 | else 33 | echo -e "-- ["$_prompt"] NOT found" 34 | echo "**FAILED**: prompts check test" && exit 1 35 | fi 36 | done 37 | echo "**PASSED**: prompts check test" 38 | } 39 | 40 | FILE_NAME=`./getBinaryName.sh` 41 | chmod +x ./dist/$FILE_NAME 42 | 43 | APP_DIR='sample-applications' 44 | BASELINE_JSON='tests-baseline/sample_applications_test.json' 45 | 46 | 47 | #for prog_langs in BASELINE_JSON, E.g. python go c_cpp java 48 | for ((idx=0;idx<`jq ". | length" $BASELINE_JSON`;idx++)); 49 | do 50 | prog_lang=`jq -r ". | keys" $BASELINE_JSON | jq -r ".[$idx]"` 51 | dl_type=`jq -r ".$prog_lang.type" $BASELINE_JSON` 52 | dl_url=`jq -r ".$prog_lang.url" $BASELINE_JSON` 53 | dl_app=`jq -r ".$prog_lang.app" $BASELINE_JSON` 54 | 55 | [ ! -d "$APP_DIR/$prog_lang" ] && mkdir -p $APP_DIR/$prog_lang 56 | 57 | if [ 'git' == $dl_type ]; then 58 | DL_BRANCH=`jq -r ".$prog_lang.branch" $BASELINE_JSON` 59 | [ ! -d "$APP_DIR/$prog_lang/$dl_app" ] && git -C $APP_DIR/$prog_lang clone --depth 1 --branch $DL_BRANCH $dl_url 60 | elif [ 'wget' == $dl_type ]; then 61 | dl_file=$(basename "$dl_url") 62 | [ ! -f "$APP_DIR/$prog_lang/$dl_file" ] && wget $dl_url -P $APP_DIR/$prog_lang/ 63 | [ ! -d "$APP_DIR/$prog_lang/$dl_app" ] && tar zxf $APP_DIR/$prog_lang/$dl_file -C $APP_DIR/$prog_lang/ 64 | else 65 | echo "wrong download type" 66 | fi 67 | 68 | echo "Running $prog_lang application to HTML report" 69 | ./dist/$FILE_NAME $APP_DIR/$prog_lang/$dl_app --output $dl_app.html 70 | 71 | echo "Running integrity check for $prog_lang application's HTML report" 72 | check_integrity $prog_lang $BASELINE_JSON "$dl_app.html" 73 | [ $? -ne 0 ] && exit 1 74 | 75 | echo "Running prompts check for $prog_lang application's HTML report" 76 | check_prompts $prog_lang $BASELINE_JSON "$dl_app.html" 77 | [ $? -ne 0 ] && exit 1 78 | done 79 | exit 0 80 | -------------------------------------------------------------------------------- /requirements-build.txt: -------------------------------------------------------------------------------- 1 | altgraph==0.17.3 2 | certifi==2023.7.22 3 | charset-normalizer==3.2.0 4 | coverage==7.2.7 5 | idna==3.4 6 | Jinja2==3.1.2 7 | MarkupSafe==2.1.3 8 | packaging==23.1 9 | progressbar33==2.4 10 | pyinstaller==5.13.1 11 | pyinstaller-hooks-contrib==2023.6 12 | pyparsing==3.1.1 13 | requests==2.31.0 14 | urllib3==2.0.7 15 | XlsxWriter==3.1.2 16 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | altgraph==0.17.3 2 | Jinja2==3.1.2 3 | MarkupSafe==2.1.3 4 | packaging==23.1 5 | progressbar33==2.4 6 | pyparsing==3.1.1 7 | XlsxWriter==3.1.2 8 | -------------------------------------------------------------------------------- /sample-projects/dotnet-samples/sample.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net6.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /sample-projects/go-samples/compatible/go.mod: -------------------------------------------------------------------------------- 1 | module sample/ampere-processor-compatible 2 | 3 | go 1.18 4 | 5 | require ( 6 | github.com/oracle/oci-go-sdk v65.36.1 7 | github.com/golang/snappy v0.0.4 8 | ) 9 | -------------------------------------------------------------------------------- /sample-projects/go-samples/compatible/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | fmt.Println("Hello world") 7 | } 8 | -------------------------------------------------------------------------------- /sample-projects/go-samples/incompatible/go.mod: -------------------------------------------------------------------------------- 1 | module sample/ampere-incompatible 2 | 3 | go 1.15 4 | 5 | require ( 6 | https://github.com/oracle/oci-go-sdk v65.39.0 7 | github.com/golang/snappy v0.0.1 8 | ) 9 | -------------------------------------------------------------------------------- /sample-projects/go-samples/incompatible/main.go: -------------------------------------------------------------------------------- 1 | // You can edit this code! 2 | // Click here and start typing. 3 | package main 4 | 5 | import "fmt" 6 | 7 | func main() { 8 | fmt.Println("Hello world") 9 | } 10 | -------------------------------------------------------------------------------- /sample-projects/java-samples/main.java: -------------------------------------------------------------------------------- 1 | class Main { 2 | public static void main(String[] args) { 3 | System.out.println("Hello World"); 4 | } 5 | } -------------------------------------------------------------------------------- /sample-projects/java-samples/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | my-test1 5 | 1.0 6 | jar 7 | Porting Advisor for Ampere Processor 8 | 9 | 10 | 11 | junit 12 | junit 13 | 4.8.2 14 | 15 | 16 | com.github.luben 17 | zstd-jni 18 | 1.1.0 19 | 20 | 21 | org.xerial.snappy 22 | snappy-java 23 | 1.1.3 24 | 25 | 26 | org.lz4 27 | lz4-java 28 | 1.4.0 29 | 30 | 31 | com.hadoop.gplcompression 32 | hadoop-lzo 33 | 0.4.17 34 | 35 | 36 | org.fusesource.leveldbjni 37 | leveldbjni-all 38 | 1.8 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /sample-projects/java-samples/submain.java: -------------------------------------------------------------------------------- 1 | class Submain { 2 | public static void main(String[] args) { 3 | System.out.println("Hello World 2"); 4 | } 5 | } -------------------------------------------------------------------------------- /sample-projects/node-samples/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sometestlibraryjs", 3 | "version": "1.2.3", 4 | "description": "Test package.json file", 5 | "scripts": { 6 | "build": "npm build" 7 | }, 8 | "dependencies": { 9 | "cors": "2.8.5", 10 | "express": "4.18.1", 11 | "rxjs": "7.5.6", 12 | "socket.io": "4.5.1", 13 | "tslib": "2.4.0" 14 | }, 15 | "devDependencies": { 16 | "@codechecks/client": "0.1.12", 17 | "@commitlint/cli": "17.0.3", 18 | "eslint": "7.32.0", 19 | "typescript": "4.7.4" 20 | } 21 | } -------------------------------------------------------------------------------- /sample-projects/python-samples/compatible/requirements.txt: -------------------------------------------------------------------------------- 1 | # Porting Advisor for Ampere test file 2 | 3 | SciPy>=1.7.2 4 | NumPy>=1.21.1 5 | -------------------------------------------------------------------------------- /sample-projects/python-samples/incompatible/requirements.txt: -------------------------------------------------------------------------------- 1 | # Porting Advisor for Ampere test file 2 | 3 | SciPy>=1.7.1 4 | NumPy 5 | -------------------------------------------------------------------------------- /sample-projects/python-samples/main.py: -------------------------------------------------------------------------------- 1 | print('Hello World') -------------------------------------------------------------------------------- /sample-projects/python-samples/submain.py: -------------------------------------------------------------------------------- 1 | print('Hello World 2') -------------------------------------------------------------------------------- /sample-projects/ruby-samples/Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gem 'rails', '~> 6.1.6.1' 4 | gem "rake", ">= 11.1" 5 | gem 'actionpack', rails_version 6 | gem 'bcrypt', '~> 3.1', '>= 3.1.14' 7 | gem "cucumber", RUBY_VERSION >= "2.5" ? "~> 5.1.2" : "~> 4.1" 8 | gem 'gc_tracer', require: false, platform: :mri 9 | gem 'gssapi', group: :kerberos 10 | gem 'mail', git: 'https://github.com/discourse/mail.git' 11 | gem "turbo-rails" 12 | 13 | group :test do 14 | gem "httpclient" 15 | 16 | if RUBY_ENGINE == "jruby" 17 | gem "jruby-openssl" 18 | end 19 | end -------------------------------------------------------------------------------- /setup-environment.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # This script looks up Python 3.10 or higher in PATH and sets up the Python Virtual Environment to install dependencies 4 | 5 | for AVAILABLE_PYTHON in $(compgen -c python | sort -u); do 6 | PYTHON_VERSION_MAJOR=$($AVAILABLE_PYTHON --version 2>/dev/null | awk -F '[ .]' 'BEGIN {OFS="."} {print $(NF-2)}') 7 | PYTHON_VERSION_MINOR=$($AVAILABLE_PYTHON --version 2>/dev/null | awk -F '[ .]' 'BEGIN {OFS="."} {print $(NF-1)}') 8 | if [[ $PYTHON_VERSION_MAJOR -ge 3 ]] && [[ $PYTHON_VERSION_MINOR -ge 10 ]]; then 9 | PYTHON3=$AVAILABLE_PYTHON 10 | echo "🐍 python3.10+ is installed" 11 | break 12 | fi 13 | done 14 | 15 | if [[ ! -z $PYTHON3 ]] 16 | then 17 | if hash pip3 18 | then 19 | echo "📦 pip is installed" 20 | else 21 | echo "**ERROR**: python pip3 not found, please install pip3" && exit 1 22 | fi 23 | 24 | if [ ! -f ".venv/bin/activate" ]; then 25 | echo "💻 Creating Python virtual environment" 26 | $PYTHON3 -m venv .venv 27 | if [ $? -ne 0 ]; then 28 | echo "**ERROR**: could not create Python Virtual Environment." && exit 1 29 | fi 30 | fi 31 | 32 | echo "💡 Making sure Python Virtual Environment is active" 33 | . .venv/bin/activate 34 | if [ $? -ne 0 ]; then 35 | echo "**ERROR**: could not activate Python Virtual Environment." && exit 1 36 | fi 37 | 38 | echo "☁️ Installing requirements" 39 | $PYTHON3 -m pip install -r requirements-build.txt 40 | if [ $? -ne 0 ]; then 41 | echo "**ERROR**: error installing required packages" && exit 1 42 | fi 43 | exit 0 44 | else 45 | echo "**ERROR**: python3.10+ is missing, please install it before running this build script" 46 | exit 1 47 | fi 48 | 49 | -------------------------------------------------------------------------------- /src/advisor/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright 2017-2020 Arm Ltd. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | SPDX-License-Identifier: Apache-2.0 17 | """ 18 | 19 | __project__ = 'porting-advisor' 20 | __version__ = '1.1.1' 21 | __summary__ = 'Produces an aarch64 porting readiness report.' 22 | __webpage__ = 'http://www.gitlab.com/arm-hpc/porting-advisor' 23 | 24 | 25 | def main(): 26 | from .main import main as real_main 27 | real_main() 28 | -------------------------------------------------------------------------------- /src/advisor/constants/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AmpereComputing/ampere-porting-advisor/510aa1345d4ac2c61e9fc3b0ab145a1ba5178dd6/src/advisor/constants/__init__.py -------------------------------------------------------------------------------- /src/advisor/constants/arch_specific_libs.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright 2017 Arm Ltd. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | SPDX-License-Identifier: Apache-2.0 17 | """ 18 | 19 | ARCH_SPECIFIC_LIBS = ['mkl', 'otherarch'] 20 | """Libraries that are not available on aarch64.""" 21 | -------------------------------------------------------------------------------- /src/advisor/constants/arch_specific_options.py: -------------------------------------------------------------------------------- 1 | """ 2 | SPDX-License-Identifier: Apache-2.0 3 | Copyright (c) 2024, Ampere Computing LLC 4 | """ 5 | 6 | X86_SPECIFIC_OPTS = ['mmx', 'sse', 'sse2', 'sse3', 'ssse3', 'sse4', 'sse4a', 'sse4.1', 'sse4.2', 'avx', 'avx2', 'avx512f', 'avx512pf', 'avx512er', 'avx512cd', 'avx512vl', 'avx512bw', 'avx512dq', 'avx512ifma', 'avx512vbmi', 'sha', 'aes', 'pclmul', 'clflushopt', 'clwb', 'fsgsbase', 'ptwrite', 'rdrnd', 'f16c', 'fma', 'pconfig', 'wbnoinvd', 'fma4', 'prfchw', 'rdpid', 'prefetchwt1', 'rdseed', 'sgx', 'xop', 'lwp', '3dnow', '3dnowa', 'popcnt', 'abm', 'adx', 'bmi', 'bmi2', 'lzcnt', 'fxsr', 'xsave', 'xsaveopt', 'xsavec', 'xsaves', 'rtm', 'hle', 'tbm', 'mwaitx', 'clzero', 'pku', 'avx512vbmi2', 'avx512bf16', 'avx512fp16', 'gfni', 'vaes', 'waitpkg', 'vpclmulqdq', 'avx512bitalg', 'movdiri', 'movdir64b', 'enqcmd', 'uintr', 'tsxldtrk', 'avx512vpopcntdq', 'avx512vp2intersect', 'avx5124fmaps', 'avx512vnni', 'avxvnni', 'avx5124vnniw', 'cldemote', 'serialize', 'amx-tile', 'amx-int8', 'amx-bf16', 'hreset', 'kl', 'widekl', 'avxifma', 'avxvnniint8', 'avxneconvert', 'cmpccxadd', 'amx-fp16', 'prefetchi', 'raoint', 'amx-complex'] 7 | """Options that are not available on aarch64.""" 8 | 9 | NEOVERSE_SPECIFIC_OPTS = ['neoverse-'] 10 | """Options for Arm Neoverse are not appropriate for AmpereOne family.""" 11 | 12 | AMPEREONE_SPECIFIC_OPTS = ['ampere1', 'ampere1a', 'ampere1b'] 13 | """Options for AmpereOne family.""" 14 | 15 | ARCH_SPECIFIC_IN_BUILD_FILES = ['meson.build', 'CMakeLists.txt', 'Makefile.in', 'Makefile.am', 'Makefile.arm64', 'Makefile.aarch64'] 16 | """Build files in aarch64 port dir can't be filter out as they might missing support for ampere1.""" 17 | -------------------------------------------------------------------------------- /src/advisor/constants/arch_strings.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright 2017-2018 Arm Ltd. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | SPDX-License-Identifier: Apache-2.0 17 | """ 18 | 19 | AARCH64_ARCHS = ['arm', 'aarch64', 'arm64', 'neon', 'sve'] 20 | 21 | NON_AARCH64_ARCHS = ['alpha', 'altivec', 'amd64', 'avx', 'avx512', 'hppa', 22 | 'i386', 'i586', 'i686', 'ia64', 'intel', 'intel64', 'ix86', 'loongarch64', 23 | 'm68k', 'microblaze', 'mips', 'nios2', 'otherarch', 'penryn', 'power', 24 | 'powerpc', 'powerpc32', 'powerpc64', 'ppc', 'ppc64', 'ppc64le', 'riscv64', 25 | 'sandybridge', 's390', 'sh', 'sifive_x280', 'sparc', 'sse', 'sse2', 'sse3', 26 | 'tile', 'x64', 'x86', 'x86_64', 'zarch', 'zen', 'zen2', 'zen3'] 27 | 28 | COMPILERS = ['clang', 'cray', 'flang', 'gcc', 'gfortran', 'gnuc', 'gnug', 'ibmcpp', 'ibmxl', 'icc', 'ifort', 29 | 'intel', 'intel_compiler', 'llvm', 'pathscale', 'pgi', 'pgic', 'sunpro', 'xlc', 'xlf'] 30 | -------------------------------------------------------------------------------- /src/advisor/filters/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AmpereComputing/ampere-porting-advisor/510aa1345d4ac2c61e9fc3b0ab145a1ba5178dd6/src/advisor/filters/__init__.py -------------------------------------------------------------------------------- /src/advisor/filters/issue_type_filter.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright 2017-2018,2020 Arm Ltd. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | SPDX-License-Identifier: Apache-2.0 17 | """ 18 | 19 | from ..scanners.scanner import Scanner 20 | 21 | class IssueTypeFilter(Scanner): 22 | """Filters issues by type.""" 23 | 24 | def __init__(self, issue_type_config): 25 | """Filters issues by type. 26 | 27 | Args: 28 | issue_type_config (IssueTypeConfig): issue type filter configuration 29 | """ 30 | self.issue_type_config = issue_type_config 31 | 32 | def finalize_report(self, report): 33 | report.issues = [issue for issue in report.issues if self.issue_type_config.include_issue_p(issue)] 34 | -------------------------------------------------------------------------------- /src/advisor/filters/other_issues_filter.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright 2017 Arm Ltd. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | SPDX-License-Identifier: Apache-2.0 17 | """ 18 | 19 | from ..reports.issues.other_issues import OtherIssues 20 | from ..scanners.scanner import Scanner 21 | 22 | 23 | class OtherIssuesFilter(Scanner): 24 | """Abbreviates long lists of similar issues.""" 25 | 26 | MAX_ISSUES_PER_FILE = 10 27 | 28 | def finalize_report(self, report): 29 | issues_per_file = {} 30 | new_issues = [] 31 | for issue in report.issues: 32 | if issue.filename: 33 | if not issue.filename in issues_per_file: 34 | issues_per_file[issue.filename] = 0 35 | if issues_per_file[issue.filename] < OtherIssuesFilter.MAX_ISSUES_PER_FILE: 36 | new_issues.append(issue) 37 | issues_per_file[issue.filename] += 1 38 | else: 39 | new_issues.append(issue) 40 | for filename in issues_per_file: 41 | if issues_per_file[filename] > OtherIssuesFilter.MAX_ISSUES_PER_FILE: 42 | new_issues.append(OtherIssues( 43 | filename, issues_per_file[filename])) 44 | report.issues = new_issues 45 | -------------------------------------------------------------------------------- /src/advisor/filters/port_filter.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright 2017 Arm Ltd. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | SPDX-License-Identifier: Apache-2.0 17 | """ 18 | 19 | from ..helpers.find_port import find_port_file 20 | from ..reports.issues.no_equivalent_issue import NoEquivalentIssue 21 | from ..reports.remarks.ported_source_files_remark import PortedSourceFilesRemark 22 | from ..scanners.scanner import Scanner 23 | 24 | 25 | class PortFilter(Scanner): 26 | """Filters out issues (e.g. inline assembly) that occur in source files 27 | for which a likely aarch64 port exists.""" 28 | 29 | def finalize_report(self, report): 30 | ported_source_files = set() 31 | for filename in report.source_files: 32 | port_file = find_port_file( 33 | filename, report.source_files, report.source_dirs) 34 | if port_file: 35 | ported_source_files.add(filename) 36 | if ported_source_files: 37 | report.add_remark(PortedSourceFilesRemark( 38 | len(ported_source_files))) 39 | report.issues = [issue for issue in report.issues 40 | if not (issue.filename and issue.filename in ported_source_files) or \ 41 | isinstance(issue, NoEquivalentIssue)] 42 | -------------------------------------------------------------------------------- /src/advisor/filters/target_os_filter.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright 2018 Arm Ltd. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | SPDX-License-Identifier: Apache-2.0 17 | """ 18 | 19 | from ..reports.issues.config_guess_issue import ConfigGuessIssue 20 | from ..scanners.scanner import Scanner 21 | from ..reports.issues.old_crt_issue import OldCrtIssue 22 | 23 | 24 | class TargetOsFilter(Scanner): 25 | """Filters out issues that are only applicable to other operating 26 | systems.""" 27 | 28 | def finalize_report(self, report): 29 | def target_os_matches(issue, target_os): 30 | if target_os == 'all': 31 | return True 32 | if isinstance(issue, OldCrtIssue) and \ 33 | target_os != 'windows': 34 | return False 35 | if isinstance(issue, ConfigGuessIssue) and \ 36 | target_os != 'linux': 37 | return False 38 | return True 39 | 40 | report.issues = [issue for issue in report.issues 41 | if target_os_matches(issue, report.target_os)] 42 | -------------------------------------------------------------------------------- /src/advisor/helpers/python/python_version_checker.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import subprocess 3 | 4 | 5 | class PythonVersionChecker(): 6 | def get_package_version(package_name): 7 | """Get the installed version of a specific package if it's installed 8 | 9 | Args: 10 | package_name: The name of the package 11 | 12 | Returns: 13 | str: The installed package version 14 | or None if it's not installed 15 | """ 16 | try: 17 | pip_process = subprocess.run(['pip3', 'show', package_name], capture_output=True) 18 | result = pip_process.stdout.decode('utf-8') 19 | lines = result.split('\n') 20 | if (len(lines) > 1): 21 | version_line = result.split('\n')[1] 22 | return version_line.split()[1] 23 | except: 24 | logging.debug('Error checking python package version.', exc_info=True) 25 | 26 | return None 27 | 28 | def get_python_version(): 29 | """Get the installed version of Python via command line 30 | 31 | Returns: 32 | str: The Python version (e.g. '3.10.4') 33 | or None if Python is not installed 34 | """ 35 | try: 36 | python_process = subprocess.run(['python3', '--version'], capture_output=True) 37 | if (python_process.stderr != ''): 38 | python_process = subprocess.run(['python', '--version'], capture_output=True) 39 | 40 | result = python_process.stdout.decode('utf-8') 41 | return result.split()[1] 42 | except: 43 | logging.debug('Error checking python version.', exc_info=True) 44 | return None 45 | 46 | def get_pip_version(): 47 | """Get the installed version of pip 48 | 49 | Returns: 50 | str: The pip version (e.g. '22.1') 51 | or None if pip is not installed 52 | """ 53 | try: 54 | pip_process = subprocess.run(['pip3', '--version'], capture_output=True) 55 | result = pip_process.stdout.decode('utf-8') 56 | return result.split()[1] 57 | except: 58 | logging.debug('Error checking pip package version.', exc_info=True) 59 | return None 60 | -------------------------------------------------------------------------------- /src/advisor/helpers/rules_loader.py: -------------------------------------------------------------------------------- 1 | import json 2 | from os import path 3 | 4 | class RulesLoader(): 5 | 6 | def get_rules(language_name): 7 | """Gets the rules object for the specified language. 8 | 9 | Args: 10 | language_name: a str with the name of the language. 11 | Returns: 12 | object: a representation of the JSON rule file for the language. None if the rules file is not found.""" 13 | rules_file = path.abspath(path.join(path.dirname(__file__), '..', 'rules', f'{language_name}.json')) 14 | if path.isfile(rules_file): 15 | with open(rules_file, 'r') as rules_file_content: 16 | rules = json.load(rules_file_content) 17 | return rules 18 | else: 19 | return None -------------------------------------------------------------------------------- /src/advisor/helpers/utils.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | 4 | class Utils(): 5 | 6 | def running_from_binary(): 7 | """ Determines if we're running as a script or binary 8 | 9 | Returns: 10 | bool: True if running as a binary 11 | False if running as a script 12 | """ 13 | return getattr(sys, 'frozen', False) and hasattr(sys, '_MEIPASS') -------------------------------------------------------------------------------- /src/advisor/helpers/version_comparer.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from packaging.version import Version, InvalidVersion 3 | 4 | class VersionComparer: 5 | def compare(version1, version2): 6 | """Compare two versions in a semantic versioning format 7 | 8 | Args: 9 | version1 (str): the first version to compare 10 | version2 (str): the second version to compare 11 | 12 | Returns: 13 | int: 1 if version1 > version2 \n 14 | -1 if version1 < version2 \n 15 | 0 if version1 = version2 16 | """ 17 | 18 | v1 = Version(version1) 19 | v2 = Version(version2) 20 | 21 | if v1 > v2: 22 | return 1 23 | elif v1 < v2: 24 | return -1 25 | else: 26 | return 0 27 | 28 | def is_valid(version): 29 | try: 30 | Version(version) 31 | except InvalidVersion: 32 | logging.debug(f'Invalid version provided: {version}') 33 | return False 34 | return True -------------------------------------------------------------------------------- /src/advisor/manifester/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AmpereComputing/ampere-porting-advisor/510aa1345d4ac2c61e9fc3b0ab145a1ba5178dd6/src/advisor/manifester/__init__.py -------------------------------------------------------------------------------- /src/advisor/manifester/dependency.py: -------------------------------------------------------------------------------- 1 | class Dependency: 2 | def __init__(self, name, version, filename, tool, lineno = None, installed_version = None) -> None: 3 | self.name = name 4 | self.version = version 5 | self.filename = filename 6 | self.tool = tool 7 | self.lineno = lineno 8 | self.installed_version = installed_version -------------------------------------------------------------------------------- /src/advisor/manifester/go_manifester.py: -------------------------------------------------------------------------------- 1 | from .regex_manifester import RegexManifester 2 | 3 | 4 | class GoManifester(RegexManifester): 5 | def __init__(self) -> None: 6 | dependency_re = r'\t(?P[\w\.\/\-\_]*) v(?P[\w\.\/-]*)' 7 | super().__init__(dependency_re, 'go') -------------------------------------------------------------------------------- /src/advisor/manifester/manifester.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from ..helpers.rules_loader import RulesLoader 3 | from ..reports.error import Error 4 | from .manifester_factory import ManifesterFactory 5 | from os import listdir, path, walk 6 | 7 | 8 | class Manifester: 9 | """This class can get the dependencies declared in a sinfle file, or in all the files from a specified folder.""" 10 | 11 | def __init__(self) -> None: 12 | """Initializes the self._supported_files variable to a dictionary like with all supported files. 13 | """ 14 | self._supported_files = [] 15 | self._supported_extensions = [] 16 | rules_path = path.abspath(path.join(path.dirname(__file__), '..', 'rules')) 17 | supported_languages = [filename[:-5] for filename in listdir(rules_path) if path.isfile(path.join(rules_path, filename)) and filename != 'sample.json'] 18 | for language in supported_languages: 19 | rules = RulesLoader.get_rules(language) 20 | dependency_files = rules['languageRules'].get('dependencyFiles', []) 21 | for file in dependency_files: 22 | if file[:1] == '*': 23 | self._supported_extensions.append(file[1:]) 24 | else: 25 | self._supported_files.extend(dependency_files) 26 | 27 | def get_dependencies(self, filename, file_object = None, report = None): 28 | """Gets the dependencies described in the provided filename. 29 | 30 | Args: 31 | filename: a str containing the filename of the file to scan 32 | file_obj: a stream of text to scan dependencies for, if available, otherwise, it will open filename 33 | report: the report being built, if any 34 | 35 | Returns: 36 | dependency array: an array of objects of type Dependency 37 | """ 38 | # get dependency_scanner 39 | # return list of dependency: 40 | # [(name, version, filename, lineno, installedversion, tool)] 41 | manifester = ManifesterFactory.get_manifester(filename) 42 | if file_object == None: 43 | with open(filename, errors='ignore') as f: 44 | try: 45 | return manifester.get_dependencies(filename, f, report) 46 | except: 47 | logging.error(f'Error while opening: {filename}.', exc_info=True) 48 | 49 | return manifester.get_dependencies(filename, file_object, report) 50 | 51 | def scan_folder(self, root_path): 52 | """Scans a folder and then reviews all supported files for their dependencies. 53 | 54 | Args: 55 | root_path: The folder containing all the source code to review.""" 56 | dependency_files = [] 57 | for dir_name, _, files in walk(root_path): 58 | for file in files: 59 | _, extension = path.splitext(file) 60 | if file in self._supported_files or extension in self._supported_extensions: 61 | dependency_files.append(path.join(dir_name, file)) 62 | 63 | dependencies = [] 64 | for file in dependency_files: 65 | dependencies.extend(self.get_dependencies(file)) 66 | return dependencies 67 | -------------------------------------------------------------------------------- /src/advisor/manifester/manifester_factory.py: -------------------------------------------------------------------------------- 1 | from os import path 2 | from .go_manifester import GoManifester 3 | from .maven_manifester import MavenManifester 4 | from .npm_manifester import NpmManifester 5 | from .nuget_manifester import NugetManifester 6 | from .pip_manifester import PipManifester 7 | from .ruby_manifester import RubyManifester 8 | 9 | class ManifesterFactory: 10 | def get_manifester(filepath): 11 | """Get the manifester corresponding to the provided file. 12 | 13 | Args: 14 | filepath: The path to the file to scan for dependencies. 15 | Returns: 16 | object: class that is able to get a dependency list from the provided file.""" 17 | filename = path.basename(filepath) 18 | match filename: 19 | case "Gemfile": 20 | return RubyManifester() 21 | case "go.mod": 22 | return GoManifester() 23 | case "package.json": 24 | return NpmManifester() 25 | case "pom.xml": 26 | return MavenManifester() 27 | case "requirements.txt": 28 | return PipManifester() 29 | case _: 30 | _, extension = path.splitext(filepath) 31 | if (extension == '.csproj'): 32 | return NugetManifester() 33 | return None -------------------------------------------------------------------------------- /src/advisor/manifester/maven_manifester.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import re 3 | import xml.etree.ElementTree as ET 4 | from .dependency import Dependency 5 | 6 | 7 | class MavenManifester: 8 | NAME_WITHOUT_NAMESPACE_RE = re.compile(r'(\{.*\})?(?P.*)') 9 | project_properties = {} 10 | 11 | def get_dependencies(self, filename, file_object, report): 12 | """Get all pip dependencies declared in the pom.xml file. 13 | 14 | Args: 15 | filename: a str containing the filename of the file to scan 16 | file_obj: a stream of text to scan dependencies for. 17 | 18 | Returns: 19 | dependency array: an array of objects of type Dependency.""" 20 | try: 21 | maven_file = ET.parse(file_object) 22 | project = maven_file.getroot() 23 | dependencies = [] 24 | 25 | # get all property values from the file. they may get used in this or other file's dependency declarations 26 | for property_node in project.findall('.//{*}properties//*'): 27 | if property_node.text and property_node.text[0].isdigit(): 28 | matches = MavenManifester.NAME_WITHOUT_NAMESPACE_RE.search(property_node.tag) 29 | property_name = matches.group('name') 30 | value = property_node.text 31 | MavenManifester.project_properties[property_name] = value 32 | 33 | for dependency in project.findall('.//{*}dependencies//{*}dependency'): 34 | artifact_id = dependency.find('.//{*}artifactId').text 35 | versionElement = dependency.find('.//{*}version') 36 | version = None 37 | if not versionElement == None: 38 | version = versionElement.text 39 | 40 | # version is a variable pointing to the properties section 41 | if version.startswith(r'${'): 42 | # get the name from '${name}' 43 | property_name = version[2:len(version)-1] 44 | if property_name in self.project_properties: 45 | version = self.project_properties[property_name] 46 | 47 | item = Dependency(artifact_id, version, filename, 'maven') 48 | dependencies.append(item) 49 | return dependencies 50 | except: 51 | logging.error(f'Failed to parse file: {filename}.', exc_info=True) -------------------------------------------------------------------------------- /src/advisor/manifester/npm_manifester.py: -------------------------------------------------------------------------------- 1 | import json 2 | from .dependency import Dependency 3 | 4 | 5 | class NpmManifester: 6 | def get_dependencies(self, filename, file_object, report): 7 | """Get all npm dependencies declared in the package.json file. 8 | 9 | Args: 10 | filename: a str containing the filename of the file to scan. 11 | file_obj: a stream of text to scan dependencies for, if available, otherwise, it will open filename. 12 | 13 | Returns: 14 | dependency array: an array of objects of type Dependency.""" 15 | dependencies = [] 16 | package_json = json.load(file_object) 17 | 18 | packages = package_json.get('dependencies', {}) 19 | packages.update(package_json.get('devDependencies', {})) 20 | for key in packages: 21 | dependency = Dependency(key, packages[key], filename, 'npm') 22 | dependencies.append(dependency) 23 | 24 | return dependencies -------------------------------------------------------------------------------- /src/advisor/manifester/nuget_manifester.py: -------------------------------------------------------------------------------- 1 | from .regex_manifester import RegexManifester 2 | 3 | 4 | class NugetManifester(RegexManifester): 5 | def __init__(self) -> None: 6 | dependency_re = r'\[\w\.\/\-\_]*)\" Version=\"(?P[\w\.\/-]*)\"\s*\/\>' 7 | super().__init__(dependency_re, 'nuget') -------------------------------------------------------------------------------- /src/advisor/manifester/pip_manifester.py: -------------------------------------------------------------------------------- 1 | from ..parsers.comment_parser import CommentParser 2 | from ..parsers.continuation_parser import ContinuationParser 3 | from ..parsers.python_comment_parser import PythonCommentParser 4 | from ..parsers.python_requirements_parser import PythonRequirementsParser 5 | from ..helpers.python.python_version_checker import PythonVersionChecker 6 | from .dependency import Dependency 7 | 8 | class PipManifester: 9 | def get_dependencies(self, filename, file_object, report): 10 | """Get all pip dependencies declared in the requirements.txt file. 11 | 12 | Args: 13 | filename: a str containing the filename of the file to scan. 14 | file_obj: a stream of text to scan dependencies for. 15 | 16 | Returns: 17 | dependency array: an array of objects of type Dependency.""" 18 | dependencies = [] 19 | continuation_parser = ContinuationParser() 20 | comment_parser = PythonCommentParser() 21 | requirements_parser = PythonRequirementsParser() 22 | 23 | for lineno, line in enumerate(file_object, 1): 24 | line = continuation_parser.parse_line(line) 25 | 26 | if not line: 27 | continue 28 | 29 | is_comment = comment_parser.parse_line(line) 30 | if is_comment: 31 | continue 32 | 33 | dependency_name, used_version, _ = requirements_parser.parse_line(line) 34 | if dependency_name: 35 | installed_version = PythonVersionChecker.get_package_version(dependency_name) 36 | dependency = Dependency(dependency_name, used_version, filename, 'pip', lineno, installed_version) 37 | dependencies.append(dependency) 38 | 39 | return dependencies -------------------------------------------------------------------------------- /src/advisor/manifester/regex_manifester.py: -------------------------------------------------------------------------------- 1 | import re 2 | from .dependency import Dependency 3 | 4 | 5 | class RegexManifester: 6 | def __init__(self, pattern, tool): 7 | self.dependencies_re = pattern 8 | self.tool = tool 9 | 10 | def get_dependencies(self, filename, file_object, report): 11 | """Get all dependencies matching the provided regular expression. 12 | 13 | Args: 14 | filename: a str containing the filename of the file to scan. 15 | file_obj: a stream of text to scan dependencies for, if available, otherwise, it will open filename. 16 | 17 | Returns: 18 | dependency array: an array of objects of type Dependency.""" 19 | file_object.seek(0) 20 | file_contents = file_object.read() 21 | matches = re.finditer(self.dependencies_re, file_contents, re.MULTILINE) 22 | dependencies = [] 23 | 24 | for _, match in enumerate(matches): 25 | dependency = Dependency(match.group('name'), match.group('version'), filename, self.tool) 26 | dependencies.append(dependency) 27 | 28 | return dependencies 29 | -------------------------------------------------------------------------------- /src/advisor/manifester/ruby_manifester.py: -------------------------------------------------------------------------------- 1 | import re 2 | from ..parsers.ruby_gem_parser import RubyGemParser 3 | from .dependency import Dependency 4 | 5 | 6 | class RubyManifester(): 7 | GEM_RE =r'gem (?P.*)' 8 | 9 | def get_dependencies(self, filename, file_object, report): 10 | """Get all ruby dependencies declared in the Gemfile file. 11 | 12 | Args: 13 | filename: a str containing the filename of the file to scan. 14 | file_obj: a stream of text to scan dependencies for, if available, otherwise, it will open filename. 15 | 16 | Returns: 17 | dependency array: an array of objects of type Dependency.""" 18 | dependencies = [] 19 | gem_parser = RubyGemParser() 20 | matches = re.finditer(RubyManifester.GEM_RE, file_object.read(), re.MULTILINE) 21 | 22 | for _, match in enumerate(matches): 23 | name, version = gem_parser.parse_line(match.group('declaration')) 24 | if name: 25 | dependency = Dependency(name, version, filename, 'ruby') 26 | dependencies.append(dependency) 27 | 28 | return dependencies 29 | -------------------------------------------------------------------------------- /src/advisor/parsers/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AmpereComputing/ampere-porting-advisor/510aa1345d4ac2c61e9fc3b0ab145a1ba5178dd6/src/advisor/parsers/__init__.py -------------------------------------------------------------------------------- /src/advisor/parsers/comment_parser.py: -------------------------------------------------------------------------------- 1 | class CommentParser: 2 | """Comment parser base.""" 3 | 4 | def __init__(self, one_line_token, multi_line_begin_token, multi_line_end_token): 5 | self._in_comment = False 6 | self._one_line_token = one_line_token 7 | self._multi_line_begin_token = multi_line_begin_token 8 | self._multi_line_end_token = multi_line_end_token 9 | 10 | def parse_line(self, line): 11 | """Parse comments in a source line 12 | 13 | Args: 14 | line (str): The line to parse. 15 | 16 | Returns: 17 | bool: Whether this line is a comment 18 | """ 19 | if line.lstrip().startswith(self._one_line_token): 20 | return True 21 | if self._in_comment: 22 | if self._multi_line_end_token in line: 23 | self._in_comment = False 24 | return True 25 | else: 26 | if line.lstrip().startswith(self._multi_line_begin_token): 27 | templine = line[2:] 28 | if not self._multi_line_end_token in templine: 29 | self._in_comment = True 30 | return True 31 | elif templine.rstrip().endswith(self._multi_line_end_token): 32 | return True 33 | return False -------------------------------------------------------------------------------- /src/advisor/parsers/continuation_parser.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright 2018 Arm Ltd. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | SPDX-License-Identifier: Apache-2.0 17 | """ 18 | 19 | class ContinuationParser: 20 | """Continuation parser. Parses lines ending with \ as continuations.""" 21 | 22 | def __init__(self): 23 | self.continuation_line = None 24 | 25 | def parse_line(self, line): 26 | """Parse continuations in a source line. 27 | 28 | Args: 29 | line (str): The line to parse. 30 | 31 | Returns: 32 | str: THe concatenated line, or None if a continuation is in progress. 33 | """ 34 | # Lines ending with \ are not processed immediately, but are concatenated together until a line not ending in 35 | # \ is encountered. This means that issues reported against the line will have the line number of the final continuation liner rather than the first. 36 | if line.endswith('\\') or line.endswith('\\\n'): 37 | if self.continuation_line: 38 | self.continuation_line += line.rstrip()[:-1] 39 | else: 40 | self.continuation_line = line.rstrip()[:-1] 41 | return None 42 | elif self.continuation_line: 43 | line = self.continuation_line + line 44 | self.continuation_line = None 45 | return line 46 | -------------------------------------------------------------------------------- /src/advisor/parsers/naive_comment_parser.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright 2018 Arm Ltd. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | SPDX-License-Identifier: Apache-2.0 17 | """ 18 | 19 | class NaiveCommentParser: 20 | """Naive comment parser.""" 21 | 22 | def __init__(self): 23 | self.in_comment = False 24 | """Are we in a multi-line comment?""" 25 | 26 | def parse_line(self, line): 27 | """Parse comments in a source line. 28 | 29 | Args: 30 | line (str): The line to parse. 31 | 32 | Returns: 33 | bool: Is this line a comment? 34 | """ 35 | if line.lstrip().startswith('//'): 36 | return True 37 | if self.in_comment: 38 | if '*/' in line: 39 | self.in_comment = False 40 | return True 41 | else: 42 | if line.lstrip().startswith('/*'): 43 | if not '*/' in line: 44 | self.in_comment = True 45 | return True 46 | elif line.rstrip().endswith('*/'): 47 | return True 48 | return False 49 | -------------------------------------------------------------------------------- /src/advisor/parsers/python_comment_parser.py: -------------------------------------------------------------------------------- 1 | from ..parsers.comment_parser import CommentParser 2 | 3 | 4 | class PythonCommentParser(CommentParser): 5 | """Python comment parser.""" 6 | 7 | def __init__(self): 8 | super(PythonCommentParser, self).__init__('#', '"""', '"""') 9 | -------------------------------------------------------------------------------- /src/advisor/parsers/python_requirements_parser.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | 4 | class PythonRequirementsParser: 5 | """This parser looks for libraries in a Python requirements.txt file to identify 6 | library and required version.""" 7 | 8 | SPECIFIER_RE = re.compile('(?P[\w-]*)\s*(?P[!<>=]*)\s*(?P[\w\.]*)') 9 | 10 | def parse_line(self, line): 11 | """Parses a library line in a requirements.txt file 12 | 13 | Args: 14 | line (str): a line in a valid format. e.g. "SciPy>=1.7.2" or "SciPy" 15 | 16 | Returns: 17 | str, str, str: dependency name, version, comparer symbol 18 | """ 19 | matches = PythonRequirementsParser.SPECIFIER_RE.search(line) 20 | return matches.group('name'), matches.group('version'), matches.group('comparer') -------------------------------------------------------------------------------- /src/advisor/parsers/ruby_gem_parser.py: -------------------------------------------------------------------------------- 1 | import re 2 | from ..helpers.version_comparer import VersionComparer 3 | 4 | class RubyGemParser: 5 | """This parser looks for libraries gems declared on the Gemfile file to identify 6 | library and required version.""" 7 | 8 | SEMVER_RE = re.compile('(\d+\.*)+') 9 | TERNARY_RE = re.compile('.+ \? (?P.+) : (?P.+)') 10 | 11 | def parse_line(self, line): 12 | """Parses a gem declaration on the Gemfile file 13 | 14 | Args: 15 | line (str): a line in a valid format. e.g. "rails', '~> 6.1.6.1" 16 | 17 | Returns: 18 | str, str: dependency name, version 19 | """ 20 | line = line.replace('\'', '"') 21 | sections = line.split(', ') 22 | 23 | if len(sections) > 0: 24 | name = sections[0].replace('"', '') 25 | 26 | version = '' 27 | ternary_versions = self._parse_ternary_expression(line) 28 | if (ternary_versions): 29 | version = self._parse_version_range(ternary_versions) 30 | else: 31 | version = self._parse_version_range(sections[1:]) 32 | 33 | return name, version 34 | 35 | def _parse_version_range(self, versions: list[str]): 36 | """Cycle through a list of versions and find the lowest one""" 37 | lowest_version = None 38 | for version in versions: 39 | match = re.search(self.SEMVER_RE, version) 40 | if match: 41 | this_version = match.group(0) 42 | if VersionComparer.is_valid(this_version): 43 | if not lowest_version or VersionComparer.compare(lowest_version, this_version) == 1: 44 | lowest_version = this_version 45 | 46 | return lowest_version 47 | 48 | def _parse_ternary_expression(self, line: str): 49 | """Receives a ternary expression and returns a list consisting of the THEN and ELSE statements""" 50 | match = re.search(self.TERNARY_RE, line) 51 | if match: 52 | return [match.group('then'), match.group('else')] 53 | else: 54 | return None 55 | -------------------------------------------------------------------------------- /src/advisor/reports/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AmpereComputing/ampere-porting-advisor/510aa1345d4ac2c61e9fc3b0ab145a1ba5178dd6/src/advisor/reports/__init__.py -------------------------------------------------------------------------------- /src/advisor/reports/csv_issue_type_count_by_file_report.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright 2020 Arm Ltd. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | SPDX-License-Identifier: Apache-2.0 17 | """ 18 | 19 | import csv 20 | from .issues.issue_types import ISSUE_TYPES 21 | from .report import Report 22 | 23 | class CsvIssueTypeCountByFileReport(Report): 24 | """Generates a CSV report with a 'issue type count by file' schema.""" 25 | 26 | def __init__(self, root_directory, target_os='linux', issue_type_config=None): 27 | """Generates a CSV report with a 'issue type count by file' schema. 28 | 29 | issue_type_config (IssueTypeConfig): issue type filter configuration. 30 | """ 31 | super().__init__(root_directory, target_os) 32 | self.issue_type_config = issue_type_config 33 | 34 | def write_items(self, output_file, items): 35 | issue_types = self.issue_type_config.filter_issue_types(ISSUE_TYPES) 36 | csv_writer = csv.writer(output_file) 37 | header = ['filename'] + [issue_type.display_name() for issue_type in issue_types] 38 | csv_writer.writerow(header) 39 | sorted_source_files = sorted(self.source_files) 40 | issue_type_totals_by_file = {} 41 | for source_file in sorted_source_files: 42 | issue_type_totals = {issue_type: 0 for issue_type in ISSUE_TYPES.values()} 43 | issue_type_totals_by_file[source_file] = issue_type_totals 44 | for item in items: 45 | issue_type_totals_by_file[item.filename][item.__class__] += 1 46 | for source_file in sorted_source_files: 47 | issue_type_totals = issue_type_totals_by_file[source_file] 48 | row = [source_file] + [issue_type_totals[issue_type] for issue_type in issue_types] 49 | csv_writer.writerow(row) 50 | -------------------------------------------------------------------------------- /src/advisor/reports/csv_report.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright 2020 Arm Ltd. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | SPDX-License-Identifier: Apache-2.0 17 | """ 18 | 19 | import csv 20 | from .report import Report 21 | 22 | class CsvReport(Report): 23 | """Generates a CSV report.""" 24 | 25 | def write_items(self, output_file, items): 26 | csv_writer = csv.writer(output_file) 27 | header = ['filename', 'function', 'line', 'issue_type', 'description'] 28 | csv_writer.writerow(header) 29 | for item in items: 30 | issue_type = item.__class__.display_name() 31 | row = [item.filename, item.function, item.lineno, issue_type, item.description] 32 | csv_writer.writerow(row) 33 | -------------------------------------------------------------------------------- /src/advisor/reports/dependencies_report.py: -------------------------------------------------------------------------------- 1 | import xlsxwriter 2 | from ..manifester.manifester import Manifester 3 | from .report import Report 4 | 5 | 6 | class DependencyReport(Report): 7 | """Generates a CSV with just the dependencies found.""" 8 | 9 | def __init__(self, root_directory, target_os='linux'): 10 | super().__init__(root_directory, target_os) 11 | self.send_filename = True 12 | self.self_process = True 13 | 14 | def process(self, args): 15 | manifester = Manifester() 16 | self.remarks.extend(manifester.scan_folder(args.root)) 17 | 18 | def write(self, output_file, report_errors=False, report_remarks=True, include_summary=False): 19 | self.write_items(output_file, self.remarks) 20 | 21 | def write_items(self, output_file, items): 22 | workbook = xlsxwriter.Workbook(output_file) 23 | classified = {} 24 | 25 | for item in items: 26 | if item.tool not in classified: 27 | classified[item.tool] = [item] 28 | else: 29 | classified[item.tool].append(item) 30 | 31 | for tool in classified: 32 | worksheet = workbook.add_worksheet(tool) 33 | self._write_header(worksheet) 34 | dependencies = classified[tool] 35 | 36 | row = 1 37 | for dependency in dependencies: 38 | if dependency.version: 39 | version = dependency.version 40 | else: 41 | version = 'LATEST' 42 | row_to_write = [dependency.name, version, dependency.tool, dependency.filename] 43 | col = 0 44 | for column in row_to_write: 45 | worksheet.write(row, col, column) 46 | col += 1 47 | row += 1 48 | workbook.close() 49 | 50 | def _write_header(self, worksheet): 51 | headers = ['component', 'version', 'origin', 'filename'] 52 | col = 0 53 | for header in headers: 54 | worksheet.write(0, col, header) 55 | col += 1 -------------------------------------------------------------------------------- /src/advisor/reports/error.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright 2017 Arm Ltd. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | SPDX-License-Identifier: Apache-2.0 17 | """ 18 | 19 | from .report_item import ReportItem 20 | 21 | 22 | class Error(ReportItem): 23 | def __init__(self, description, filename=None, lineno=None): 24 | super().__init__(description=description, filename=filename, 25 | lineno=lineno, item_type=ReportItem.ERROR) 26 | -------------------------------------------------------------------------------- /src/advisor/reports/html_report.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright 2017 Arm Ltd. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | SPDX-License-Identifier: Apache-2.0 17 | """ 18 | 19 | from datetime import datetime 20 | from jinja2 import Environment, FileSystemLoader 21 | from os import getcwd, path 22 | from .. import __version__ 23 | from .report import Report 24 | 25 | 26 | class HtmlReport(Report): 27 | """Generates an HTML report from a template.""" 28 | 29 | def write(self, output_file, report_errors=True, report_remarks=True, include_summary=True): 30 | """ Override write to report all items.""" 31 | super().write(output_file, report_errors=report_errors, 32 | report_remarks=report_remarks, include_summary=include_summary) 33 | 34 | def write_items(self, output_file, items): 35 | templates_folder = path.abspath(path.join(path.dirname(__file__), '..', 'templates')) 36 | 37 | env = Environment( 38 | loader=FileSystemLoader(templates_folder), 39 | autoescape=True 40 | ) 41 | template = env.get_template('template.html') 42 | 43 | directory_name = path.normpath(self.root_directory) 44 | if (directory_name in ['.', './']): 45 | directory_name = path.basename(getcwd()) 46 | 47 | base_name = path.basename(directory_name) 48 | report_date = datetime.today().strftime('%Y-%m-%d %H:%M:%S') 49 | 50 | rendered = template.render( 51 | root_directory=directory_name, 52 | root_directory_basename=base_name, 53 | report_date=report_date, 54 | tool_version=__version__, 55 | items=items) 56 | output_file.write(rendered) 57 | -------------------------------------------------------------------------------- /src/advisor/reports/issues/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AmpereComputing/ampere-porting-advisor/510aa1345d4ac2c61e9fc3b0ab145a1ba5178dd6/src/advisor/reports/issues/__init__.py -------------------------------------------------------------------------------- /src/advisor/reports/issues/arch_specific_build_option_issue.py: -------------------------------------------------------------------------------- 1 | """ 2 | SPDX-License-Identifier: Apache-2.0 3 | Copyright (c) 2024, Ampere Computing LLC 4 | """ 5 | 6 | from .issue import Issue 7 | from ..localization import _ 8 | from ..report_item import ReportItem 9 | 10 | 11 | class ArchSpecificBuildOptionIssue(Issue): 12 | def __init__(self, filename, lineno, opt_name): 13 | description = _("architecture-specific build option is not available on aarch64: -m%s") % \ 14 | opt_name 15 | super().__init__(description=description, filename=filename, 16 | lineno=lineno) 17 | 18 | class NeoverseSpecificBuildOptionIssue(Issue): 19 | def __init__(self, filename, lineno, opt_name, opt_value): 20 | description = (_("neoverse-specific build option may not appropriate for AmpereOne: " 21 | "-m%s=%s, see https://amperecomputing.com/tutorials/gcc-guide-ampere-processors for more details.") \ 22 | % (opt_name, opt_value)) 23 | super().__init__(description=description, filename=filename, 24 | lineno=lineno, item_type=ReportItem.NEUTRAL) 25 | 26 | class AmpereoneSpecificBuildOptionIssue(Issue): 27 | def __init__(self, filename, lineno): 28 | description = (_("Missing build option for AmpereOne, see " 29 | "https://amperecomputing.com/tutorials/gcc-guide-ampere-processors for more details.")) 30 | super().__init__(description=description, filename=filename, 31 | lineno=lineno, item_type=ReportItem.NEUTRAL) 32 | -------------------------------------------------------------------------------- /src/advisor/reports/issues/arch_specific_library_issue.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright 2017,2020 Arm Ltd. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | SPDX-License-Identifier: Apache-2.0 17 | """ 18 | 19 | from .issue import Issue 20 | from ..localization import _ 21 | 22 | 23 | class ArchSpecificLibraryIssue(Issue): 24 | def __init__(self, filename, lineno, lib_name): 25 | description = _("architecture-specific library is not available on aarch64: lib%s") % \ 26 | lib_name 27 | super().__init__(description=description, filename=filename, 28 | lineno=lineno) 29 | -------------------------------------------------------------------------------- /src/advisor/reports/issues/asm_source_issue.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright 2017 Arm Ltd. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | SPDX-License-Identifier: Apache-2.0 17 | """ 18 | 19 | from .issue import Issue 20 | from ..localization import _ 21 | 22 | 23 | class AsmSourceIssue(Issue): 24 | def __init__(self, filename): 25 | description = _("architecture-specific assembly source file") 26 | super().__init__(description=description, filename=filename) 27 | -------------------------------------------------------------------------------- /src/advisor/reports/issues/build_command_issue.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright 2017-2018 Arm Ltd. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | SPDX-License-Identifier: Apache-2.0 17 | """ 18 | 19 | from ..localization import _ 20 | from .cross_compile_issue import CrossCompileIssue 21 | 22 | 23 | class BuildCommandIssue(CrossCompileIssue): 24 | def __init__(self, filename, lineno, target): 25 | self.target = target 26 | description = _("build target is run at build time (not cross-compile friendly): %s") % \ 27 | target 28 | super().__init__(description=description, filename=filename, 29 | lineno=lineno) 30 | -------------------------------------------------------------------------------- /src/advisor/reports/issues/compiler_specific_issue.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright 2017-2018 Arm Ltd. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | SPDX-License-Identifier: Apache-2.0 17 | """ 18 | 19 | from .issue import Issue 20 | from ..localization import _ 21 | from ..report_item import ReportItem 22 | 23 | 24 | class CompilerSpecificIssue(Issue): 25 | def __init__(self, filename, lineno, compiler, function=None): 26 | description = _("compiler specific code: %s") % compiler 27 | super().__init__(description=description, filename=filename, 28 | lineno=lineno, item_type=ReportItem.NEUTRAL, 29 | function=function) 30 | -------------------------------------------------------------------------------- /src/advisor/reports/issues/config_guess_issue.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright 2017 Arm Ltd. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | SPDX-License-Identifier: Apache-2.0 17 | """ 18 | 19 | from .issue import Issue 20 | from ..localization import _ 21 | 22 | 23 | class ConfigGuessIssue(Issue): 24 | def __init__(self, filename): 25 | description = _( 26 | 'autoconf config.guess needs updating to recognize aarch64 architecture') 27 | super().__init__(filename=filename, 28 | description=description) 29 | -------------------------------------------------------------------------------- /src/advisor/reports/issues/cross_compile_issue.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright 2018 Arm Ltd. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | SPDX-License-Identifier: Apache-2.0 17 | """ 18 | 19 | from .issue import Issue 20 | from ..localization import _ 21 | from ..report_item import ReportItem 22 | 23 | 24 | class CrossCompileIssue(Issue): 25 | def __init__(self, description, filename=None, lineno=None, item_type=ReportItem.NEUTRAL, function=None): 26 | super().__init__(description, filename=filename, lineno=lineno, item_type=item_type, function=function) 27 | -------------------------------------------------------------------------------- /src/advisor/reports/issues/define_other_arch_issue.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright 2018 Arm Ltd. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | SPDX-License-Identifier: Apache-2.0 17 | """ 18 | 19 | from .issue import Issue 20 | from ..localization import _ 21 | 22 | 23 | class DefineOtherArchIssue(Issue): 24 | def __init__(self, filename, lineno, define): 25 | description = _("hard-coded architecture-specific define: %s") % \ 26 | define 27 | super().__init__(description=description, filename=filename, 28 | lineno=lineno) 29 | -------------------------------------------------------------------------------- /src/advisor/reports/issues/dependency_version_issue.py: -------------------------------------------------------------------------------- 1 | from .issue import Issue 2 | 3 | 4 | class DependencyVersionIssue(Issue): 5 | def __init__(self, filename, lineno, library_name, current_version, suggested_version): 6 | description = f'using dependency library {library_name} version {current_version}. upgrade to at least version {suggested_version}' 7 | super().__init__(description=description, filename=filename, 8 | lineno=lineno) -------------------------------------------------------------------------------- /src/advisor/reports/issues/host_cpu_detection_issue.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright 2018 Arm Ltd. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | SPDX-License-Identifier: Apache-2.0 17 | """ 18 | 19 | from ..localization import _ 20 | from .cross_compile_issue import CrossCompileIssue 21 | 22 | 23 | class HostCpuDetectionIssue(CrossCompileIssue): 24 | def __init__(self, filename, lineno, condition): 25 | description = _("condition checks host CPU (not cross-compile friendly): %s") % \ 26 | condition 27 | super().__init__(description=description, filename=filename, 28 | lineno=lineno) 29 | -------------------------------------------------------------------------------- /src/advisor/reports/issues/inline_asm_issue.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright 2017-2018 Arm Ltd. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | SPDX-License-Identifier: Apache-2.0 17 | """ 18 | 19 | from .issue import Issue 20 | from ..localization import _ 21 | 22 | 23 | class InlineAsmIssue(Issue): 24 | def __init__(self, filename, lineno, function=None): 25 | description = _("architecture-specific inline assembly") 26 | super().__init__(description=description, filename=filename, 27 | lineno=lineno, 28 | function=function) 29 | -------------------------------------------------------------------------------- /src/advisor/reports/issues/intrinsic_issue.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright 2017-2018,2020 Arm Ltd. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | SPDX-License-Identifier: Apache-2.0 17 | """ 18 | 19 | from .issue import Issue 20 | from ..localization import _ 21 | 22 | 23 | class IntrinsicIssue(Issue): 24 | def __init__(self, filename, lineno, intrinsic, function=None, description=None): 25 | if not description: 26 | description = _("architecture-specific intrinsic: %s") % intrinsic 27 | super().__init__(description=description, filename=filename, 28 | lineno=lineno, 29 | function=function) 30 | 31 | class Avx256IntrinsicIssue(IntrinsicIssue): 32 | """Class for AVX-256 intrinsic issues""" 33 | def __init__(self, filename, lineno, intrinsic, function=None): 34 | description = _("architecture-specific AVX-256 intrinsic: %s") % intrinsic 35 | super().__init__(filename, lineno, intrinsic, function, description) 36 | 37 | class Avx512IntrinsicIssue(IntrinsicIssue): 38 | """Class for AVX-512 intrinsic issues""" 39 | def __init__(self, filename, lineno, intrinsic, function=None): 40 | description = _("architecture-specific AVX-512 intrinsic: %s") % intrinsic 41 | super().__init__(filename, lineno, intrinsic, function, description) 42 | -------------------------------------------------------------------------------- /src/advisor/reports/issues/issue.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright 2020 Arm Ltd. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | SPDX-License-Identifier: Apache-2.0 17 | """ 18 | 19 | import re 20 | from ..report_item import ReportItem 21 | 22 | class Issue(ReportItem): 23 | """Base class for issues.""" 24 | 25 | def __init__(self, description, filename=None, lineno=None, item_type=ReportItem.NEGATIVE, function=None): 26 | super().__init__(description, filename=filename, lineno=lineno, item_type=item_type, function=function) 27 | 28 | @classmethod 29 | def display_name(cls): 30 | """Return the display name for the given issue class.""" 31 | return re.sub('Issue$', '', cls.__name__) 32 | -------------------------------------------------------------------------------- /src/advisor/reports/issues/issue_types.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright 2017-2018 Arm Ltd. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | SPDX-License-Identifier: Apache-2.0 17 | """ 18 | 19 | import importlib 20 | import os 21 | import pkgutil 22 | from .issue import Issue 23 | 24 | pkg_dir = os.path.dirname(__file__) 25 | for (module_loader, name, ispkg) in pkgutil.iter_modules([pkg_dir]): 26 | if name.endswith('_issue'): 27 | importlib.import_module('.' + name, __package__) 28 | 29 | def _transitive_closure(cls): 30 | subclasses = cls.__subclasses__() 31 | return subclasses + [descendant for subcls in subclasses for descendant in _transitive_closure(subcls)] 32 | 33 | ISSUE_TYPES = {cls.display_name(): cls for cls in _transitive_closure(Issue)} 34 | 35 | -------------------------------------------------------------------------------- /src/advisor/reports/issues/native_methods_issue.py: -------------------------------------------------------------------------------- 1 | from .issue import Issue 2 | from ..report_item import ReportItem 3 | 4 | 5 | class NativeMethodsIssue(Issue): 6 | def __init__(self, native_methods, filename=None, lineno=None, item_type=ReportItem.NEGATIVE, function=None): 7 | description = f'JAR has native methods but no libraries found for aarch64/Linux. {native_methods}' 8 | super().__init__(description, filename, lineno, item_type, function) -------------------------------------------------------------------------------- /src/advisor/reports/issues/no_equivalent_inline_asm_issue.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright 2018 Arm Ltd. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | SPDX-License-Identifier: Apache-2.0 17 | """ 18 | 19 | from ..localization import _ 20 | from .no_equivalent_issue import NoEquivalentIssue 21 | 22 | 23 | class NoEquivalentInlineAsmIssue(NoEquivalentIssue): 24 | def __init__(self, filename, lineno, function=None): 25 | description = _("architecture-specific inline assembly used on other architecture(s), but not on Arm") 26 | super().__init__(description=description, filename=filename, 27 | lineno=lineno, 28 | function=function) 29 | -------------------------------------------------------------------------------- /src/advisor/reports/issues/no_equivalent_intrinsic_issue.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright 2018 Arm Ltd. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | SPDX-License-Identifier: Apache-2.0 17 | """ 18 | 19 | from ..localization import _ 20 | from .no_equivalent_issue import NoEquivalentIssue 21 | 22 | 23 | class NoEquivalentIntrinsicIssue(NoEquivalentIssue): 24 | def __init__(self, filename, lineno, intrinsic, function=None): 25 | description = _("architecture-specific intrinsic used on other architecture(s), but not on Arm: %s") % intrinsic 26 | super().__init__(description=description, filename=filename, 27 | lineno=lineno, 28 | function=function) 29 | -------------------------------------------------------------------------------- /src/advisor/reports/issues/no_equivalent_issue.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright 2018 Arm Ltd. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | SPDX-License-Identifier: Apache-2.0 17 | """ 18 | 19 | from .issue import Issue 20 | from ..localization import _ 21 | from ..report_item import ReportItem 22 | 23 | 24 | class NoEquivalentIssue(Issue): 25 | def __init__(self, description, filename=None, lineno=None, item_type=ReportItem.NEUTRAL, function=None): 26 | super().__init__(description, filename=filename, lineno=lineno, item_type=item_type, function=function) 27 | -------------------------------------------------------------------------------- /src/advisor/reports/issues/old_crt_issue.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright 2018 Arm Ltd. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | SPDX-License-Identifier: Apache-2.0 17 | """ 18 | 19 | from .issue import Issue 20 | from ..localization import _ 21 | 22 | 23 | class OldCrtIssue(Issue): 24 | def __init__(self, filename, lineno, lib_name): 25 | description = _("links with old C runtime, prefer Universal CRT: %s") % \ 26 | lib_name 27 | super().__init__(description=description, filename=filename, 28 | lineno=lineno) 29 | -------------------------------------------------------------------------------- /src/advisor/reports/issues/other_issues.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright 2017 Arm Ltd. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | SPDX-License-Identifier: Apache-2.0 17 | """ 18 | 19 | from ..localization import _ 20 | from ..report_item import ReportItem 21 | 22 | 23 | class OtherIssues(ReportItem): 24 | def __init__(self, filename, count): 25 | description = _("%d other issues") % count 26 | super().__init__(description=description, 27 | filename=filename, 28 | item_type=ReportItem.NEGATIVE) 29 | -------------------------------------------------------------------------------- /src/advisor/reports/issues/pragma_simd_issue.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright 2017-2018 Arm Ltd. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | SPDX-License-Identifier: Apache-2.0 17 | """ 18 | 19 | from .issue import Issue 20 | from ..localization import _ 21 | from ..report_item import ReportItem 22 | 23 | 24 | class PragmaSimdIssue(Issue): 25 | def __init__(self, filename, lineno, pragma, function=None): 26 | description = _("simd pragma: %s") % pragma 27 | super().__init__(description=description, filename=filename, 28 | lineno=lineno, item_type=ReportItem.NEUTRAL, 29 | function=function) 30 | -------------------------------------------------------------------------------- /src/advisor/reports/issues/preprocessor_error_issue.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright 2017-2018 Arm Ltd. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | SPDX-License-Identifier: Apache-2.0 17 | """ 18 | 19 | from .issue import Issue 20 | from ..localization import _ 21 | 22 | 23 | class PreprocessorErrorIssue(Issue): 24 | def __init__(self, filename, lineno, description, function=None): 25 | description = _('preprocessor error on aarch64: %s') % description 26 | super().__init__(description=description, filename=filename, 27 | lineno=lineno, 28 | function=function) 29 | -------------------------------------------------------------------------------- /src/advisor/reports/issues/unsupported_dependency_issue.py: -------------------------------------------------------------------------------- 1 | from .issue import Issue 2 | 3 | 4 | class UnsupportedDependencyIssue(Issue): 5 | def __init__(self, filename, library_name): 6 | description = f'dependency library: {library_name} is not supported on Ampere Processors' 7 | super().__init__(description, filename) 8 | -------------------------------------------------------------------------------- /src/advisor/reports/json_report.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright 2020 Arm Ltd. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | SPDX-License-Identifier: Apache-2.0 17 | """ 18 | 19 | import json 20 | from .report import Report 21 | 22 | class JsonReport(Report): 23 | """Generates a JSON report.""" 24 | 25 | def __init__(self, root_directory, target_os='linux', issue_type_config=None): 26 | """Generates a JSON report. 27 | 28 | issue_type_config (IssueTypeConfig): issue type filter configuration. 29 | """ 30 | super().__init__(root_directory, target_os) 31 | self.issue_types = issue_type_config.config_string if issue_type_config else None 32 | 33 | def write_items(self, output_file, items): 34 | # munge 'self' fields so it can be serialized 35 | self.source_dirs = list(self.source_dirs) 36 | self.issues = [i.__class__.__name__ + ': ' + str(i) for i in self.issues] 37 | self.errors = [i.__class__.__name__ + ': ' + str(i) for i in self.errors] 38 | self.remarks = [i.__class__.__name__ + ': ' + str(i) for i in self.remarks] 39 | print(json.dumps(self.__dict__, sort_keys=True, indent=4), file=output_file) 40 | -------------------------------------------------------------------------------- /src/advisor/reports/localization.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright 2017 Arm Ltd. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | SPDX-License-Identifier: Apache-2.0 17 | """ 18 | 19 | import gettext 20 | import os 21 | from .. import __name__ as PACKAGE 22 | 23 | localedir = os.path.join(os.path.dirname(__file__), 'locale') 24 | cat = gettext.Catalog(PACKAGE, localedir=localedir, fallback=True) 25 | _ = cat.gettext 26 | -------------------------------------------------------------------------------- /src/advisor/reports/remarks/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AmpereComputing/ampere-porting-advisor/510aa1345d4ac2c61e9fc3b0ab145a1ba5178dd6/src/advisor/reports/remarks/__init__.py -------------------------------------------------------------------------------- /src/advisor/reports/remarks/config_guess_remark.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright 2017 Arm Ltd. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | SPDX-License-Identifier: Apache-2.0 17 | """ 18 | 19 | from ..localization import _ 20 | from ..report_item import ReportItem 21 | 22 | 23 | class ConfigGuessRemark(ReportItem): 24 | def __init__(self, filename): 25 | description = _( 26 | 'autoconf config.guess recognizes aarch64 architecture') 27 | super().__init__(filename=filename, 28 | description=description, 29 | item_type=ReportItem.POSITIVE) 30 | -------------------------------------------------------------------------------- /src/advisor/reports/remarks/dependency_version_remark.py: -------------------------------------------------------------------------------- 1 | from ..report_item import ReportItem 2 | from .version_remark import VersionRemark 3 | 4 | 5 | class DependencyVersionRemark(VersionRemark): 6 | def __init__(self, 7 | filename, 8 | lineno, 9 | library_name, 10 | details=None, 11 | min_version=None, 12 | recommended_version=None, 13 | installed_version=None, 14 | details_url=None, 15 | override_text=None, 16 | item_type=ReportItem.NEUTRAL): 17 | 18 | description = f'dependency library {library_name} is present.' 19 | 20 | super().__init__(filename=filename, 21 | lineno=lineno, 22 | name=library_name, 23 | description=description, 24 | details=details, 25 | min_version=min_version, 26 | recommended_version=recommended_version, 27 | installed_version=installed_version, 28 | details_url=details_url, 29 | override_text=override_text, 30 | item_type=item_type) -------------------------------------------------------------------------------- /src/advisor/reports/remarks/files_scanned_remark.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright 2017 Arm Ltd. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | SPDX-License-Identifier: Apache-2.0 17 | """ 18 | 19 | from ..localization import _ 20 | from ..report_item import ReportItem 21 | 22 | 23 | class FilesScannedRemark(ReportItem): 24 | def __init__(self, count): 25 | description = _( 26 | '%d files scanned.') % count 27 | super().__init__(description=description, 28 | item_type=ReportItem.SUMMARY) 29 | -------------------------------------------------------------------------------- /src/advisor/reports/remarks/language_version_remark.py: -------------------------------------------------------------------------------- 1 | from ..report_item import ReportItem 2 | from .version_remark import VersionRemark 3 | 4 | 5 | class LanguageVersionRemark(VersionRemark): 6 | def __init__(self, 7 | language_name, 8 | min_version, 9 | description=None, 10 | details=None, 11 | recommended_version=None, 12 | installed_version=None, 13 | details_url=None, 14 | override_text=None, 15 | item_type=ReportItem.NEUTRAL): 16 | 17 | description = f'detected {language_name} code.' 18 | 19 | super().__init__(None, 20 | None, 21 | name=language_name, 22 | description=description, 23 | details=details, 24 | min_version=min_version, 25 | recommended_version=recommended_version, 26 | installed_version=installed_version, 27 | details_url=details_url, 28 | override_text=override_text, 29 | item_type=item_type) -------------------------------------------------------------------------------- /src/advisor/reports/remarks/no_issues_found_remark.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright 2017 Arm Ltd. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | SPDX-License-Identifier: Apache-2.0 17 | """ 18 | 19 | from ..localization import _ 20 | from ..report_item import ReportItem 21 | 22 | 23 | class NoIssuesFoundRemark(ReportItem): 24 | def __init__(self): 25 | description = _("No issues found.") 26 | super().__init__(description=description, item_type=ReportItem.POSITIVE) 27 | -------------------------------------------------------------------------------- /src/advisor/reports/remarks/ported_inline_asm_remark.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright 2017 Arm Ltd. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | SPDX-License-Identifier: Apache-2.0 17 | """ 18 | 19 | from ..localization import _ 20 | from ..report_item import ReportItem 21 | 22 | 23 | class PortedInlineAsmRemark(ReportItem): 24 | def __init__(self, ported_asm_source_files): 25 | description = _("%d inline assembly statements or intrinsics already have aarch64 equivalents") % \ 26 | ported_asm_source_files 27 | super().__init__(description=description, item_type=ReportItem.POSITIVE) 28 | -------------------------------------------------------------------------------- /src/advisor/reports/remarks/ported_source_files_remark.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright 2017 Arm Ltd. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | SPDX-License-Identifier: Apache-2.0 17 | """ 18 | 19 | from ..localization import _ 20 | from ..report_item import ReportItem 21 | 22 | 23 | class PortedSourceFilesRemark(ReportItem): 24 | def __init__(self, ported_source_files): 25 | description = _("%d source files are already ported to aarch64") % \ 26 | ported_source_files 27 | super().__init__(description=description, item_type=ReportItem.POSITIVE) 28 | -------------------------------------------------------------------------------- /src/advisor/reports/remarks/special_instructions_remark.py: -------------------------------------------------------------------------------- 1 | from ..report_item import ReportItem 2 | 3 | 4 | class SpecialInstructionsRemark(ReportItem): 5 | def __init__(self, filename, library_name, special_instructions, details_url = None): 6 | description = f'using dependency library {library_name}.' 7 | 8 | if (special_instructions): 9 | description = ' '.join([description, special_instructions]) 10 | if (details_url): 11 | description = ' '.join([description, f' more info at: {details_url}']) 12 | 13 | super().__init__(description, filename, item_type=ReportItem.NEUTRAL) 14 | -------------------------------------------------------------------------------- /src/advisor/reports/remarks/tool_version_remark.py: -------------------------------------------------------------------------------- 1 | from ..report_item import ReportItem 2 | from .version_remark import VersionRemark 3 | 4 | 5 | class ToolVersionRemark(VersionRemark): 6 | def __init__(self, 7 | description, 8 | min_version, 9 | recommended_version=None, 10 | installed_version=None, 11 | details_url=None, 12 | override_text=None, 13 | item_type=ReportItem.POSITIVE): 14 | 15 | super().__init__(filename=None, 16 | lineno=None, 17 | name=None, 18 | description=description, 19 | details=None, 20 | min_version=min_version, 21 | recommended_version=recommended_version, 22 | installed_version=installed_version, 23 | details_url=details_url, 24 | override_text=override_text, 25 | item_type=item_type) -------------------------------------------------------------------------------- /src/advisor/reports/remarks/version_remark.py: -------------------------------------------------------------------------------- 1 | from ..report_item import ReportItem 2 | from ...helpers.version_comparer import VersionComparer 3 | 4 | 5 | class VersionRemark(ReportItem): 6 | def __init__(self, 7 | filename, 8 | lineno, 9 | name, 10 | description=None, 11 | details=None, 12 | min_version=None, 13 | recommended_version=None, 14 | installed_version=None, 15 | details_url=None, 16 | override_text=None, 17 | item_type=ReportItem.NEUTRAL): 18 | 19 | if override_text: 20 | description = override_text 21 | 22 | else: 23 | meets_min_version = False 24 | if description == None: 25 | description = f'{name} is present.' 26 | 27 | if details: 28 | description = ' '.join([description, details]) 29 | if min_version: 30 | description = ' '.join([description, f'min version {min_version} is required.']) 31 | if installed_version and VersionComparer.compare(installed_version, min_version) >= 0: 32 | item_type = ReportItem.POSITIVE 33 | meets_min_version = True 34 | elif installed_version: 35 | item_type = ReportItem.NEGATIVE 36 | if recommended_version: 37 | description = ' '.join([description, f'version {recommended_version} or above is recommended.']) 38 | if installed_version and VersionComparer.compare(installed_version, recommended_version) >= 0: 39 | item_type = ReportItem.POSITIVE 40 | elif installed_version and not meets_min_version: 41 | item_type = ReportItem.NEGATIVE 42 | if installed_version: 43 | description = ' '.join([description, f'we detected that you have version {installed_version}.']) 44 | if details_url: 45 | description = ' '.join([description, f'see {details_url} for more details.']) 46 | 47 | super().__init__(filename=filename, 48 | description=description, 49 | lineno=lineno, 50 | item_type=item_type) -------------------------------------------------------------------------------- /src/advisor/reports/report.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright 2017-2018 Arm Ltd. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | SPDX-License-Identifier: Apache-2.0 17 | """ 18 | 19 | import os 20 | from .remarks.files_scanned_remark import FilesScannedRemark 21 | from .remarks.no_issues_found_remark import NoIssuesFoundRemark 22 | from .report_item import ReportItem 23 | 24 | class Report: 25 | def __init__(self, root_directory, target_os='linux'): 26 | self.issues = [] 27 | self.errors = [] 28 | self.remarks = [] 29 | self.root_directory = root_directory 30 | self.source_files = [] 31 | self.source_dirs = set() 32 | self.target_os = target_os 33 | self.open_text_mode = 'w' 34 | self.send_filename = False 35 | self.self_process = False 36 | 37 | def add_source_file(self, source_file): 38 | self.source_files.append(source_file) 39 | self.source_dirs.add(os.path.dirname(source_file)) 40 | 41 | def add_issue(self, item): 42 | self.issues.append(item) 43 | 44 | def add_remark(self, item): 45 | self.remarks.append(item) 46 | 47 | def add_error(self, error): 48 | self.errors.append(error) 49 | 50 | def write(self, output_file, report_errors=False, report_remarks=False, include_summary=False): 51 | items = {} 52 | for item_type in ReportItem.TYPES: 53 | items[item_type] = [] 54 | all_items = [] 55 | if report_remarks: 56 | all_items += self.remarks 57 | all_items += self.issues 58 | if report_errors: 59 | all_items += self.errors 60 | for item in all_items: 61 | items[item.item_type].append(item) 62 | if include_summary: 63 | items[ReportItem.SUMMARY].append( 64 | FilesScannedRemark(len(self.source_files))) 65 | if not items[ReportItem.NEGATIVE] and not items[ReportItem.NEUTRAL] and report_remarks: 66 | items[ReportItem.POSITIVE].append(NoIssuesFoundRemark()) 67 | sorted_items = [] 68 | for item_type in ReportItem.TYPES: 69 | sorted_items += sorted(items[item_type], key=lambda item: ( 70 | (item.filename if item.filename else '') + ':' + item.description)) 71 | self.write_items(output_file, sorted_items) 72 | 73 | def write_items(self, output_file, items): 74 | for item in items: 75 | print(item, file=output_file) 76 | -------------------------------------------------------------------------------- /src/advisor/reports/report_factory.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright 2020 Arm Ltd. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | SPDX-License-Identifier: Apache-2.0 17 | """ 18 | 19 | from .csv_report import CsvReport 20 | from .csv_issue_type_count_by_file_report import CsvIssueTypeCountByFileReport 21 | from .dependencies_report import DependencyReport 22 | from .json_report import JsonReport 23 | from .html_report import HtmlReport 24 | from .text_report import TextReport 25 | from enum import Enum 26 | 27 | class ReportOutputFormat(Enum): 28 | AUTO = 'auto' 29 | CSV_ISSUE_TYPE_COUNT_BY_FILE = 'csv_issue_type_count_by_file' 30 | CSV = 'csv' 31 | JSON = 'json' 32 | HTML = 'html' 33 | TEXT = 'text' 34 | DEPENDENCIES = 'dependencies' 35 | DEFAULT = AUTO 36 | 37 | class ReportFactory: 38 | """ Factory class for report output formats. """ 39 | 40 | _OUTPUT_FORMAT_FOR_EXTENSION = { 41 | 'json': ReportOutputFormat.JSON, 42 | 'html': ReportOutputFormat.HTML, 43 | 'htm' : ReportOutputFormat.HTML, 44 | 'txt' : ReportOutputFormat.TEXT, 45 | 'csv' : ReportOutputFormat.CSV 46 | } 47 | """ Dictionary mapping file extension to output format. 48 | 49 | Used to decide the output format from the output file name when the 50 | report output format is 'auto'. """ 51 | 52 | def output_format_for_extension(self, extension): 53 | """ Choose an output format based on the given file name extension. """ 54 | return ReportFactory._OUTPUT_FORMAT_FOR_EXTENSION.get(extension.lower(), None) 55 | 56 | def createReport(self, root_directory, target_os='linux', issue_type_config=None, output_format=ReportOutputFormat.TEXT): 57 | match output_format: 58 | case ReportOutputFormat.TEXT: 59 | report = TextReport(root_directory, target_os=target_os) 60 | case ReportOutputFormat.HTML: 61 | report = HtmlReport(root_directory, target_os=target_os) 62 | case ReportOutputFormat.CSV: 63 | report = CsvReport(root_directory, target_os=target_os) 64 | case ReportOutputFormat.CSV_ISSUE_TYPE_COUNT_BY_FILE: 65 | report = CsvIssueTypeCountByFileReport(root_directory, target_os=target_os, issue_type_config=issue_type_config) 66 | case ReportOutputFormat.JSON: 67 | report = JsonReport(root_directory, target_os=target_os, issue_type_config=issue_type_config) 68 | case ReportOutputFormat.DEPENDENCIES: 69 | report = DependencyReport(root_directory) 70 | case _: 71 | raise ValueError(output_format) 72 | return report 73 | -------------------------------------------------------------------------------- /src/advisor/reports/report_item.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright 2017-2018 Arm Ltd. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | SPDX-License-Identifier: Apache-2.0 17 | """ 18 | 19 | from .localization import _ 20 | 21 | 22 | class ReportItem: 23 | SUMMARY = 'summary' 24 | POSITIVE = 'positive' 25 | NEUTRAL = 'neutral' 26 | NEGATIVE = 'negative' 27 | ERROR = 'error' 28 | TYPES = [SUMMARY, POSITIVE, 29 | NEGATIVE, NEUTRAL, 30 | ERROR] 31 | 32 | def __init__(self, description, filename=None, lineno=None, item_type=NEUTRAL, function=None): 33 | self.filename = filename 34 | self.lineno = lineno 35 | self.description = description 36 | self.item_type = item_type 37 | self.function = function 38 | 39 | def __str__(self): 40 | if self.lineno: 41 | if self.function: 42 | return _('%(file)s:%(lineno)s (%(function)s): %(description)s') % \ 43 | {'file': self.filename, 44 | 'lineno': self.lineno, 45 | 'function': self.function, 46 | 'description': self.description} 47 | else: 48 | return _('%(file)s:%(lineno)s: %(description)s') % \ 49 | {'file': self.filename, 50 | 'lineno': self.lineno, 51 | 'description': self.description} 52 | elif self.filename: 53 | if self.function: 54 | return _('%(file)s (%(function)s): %(description)s') % \ 55 | {'file': self.filename, 56 | 'function': self.function, 57 | 'description': self.description} 58 | else: 59 | return _('%(file)s: %(description)s') % \ 60 | {'file': self.filename, 61 | 'description': self.description} 62 | else: 63 | if self.function: 64 | return _('%(function)s: %(description)s') % \ 65 | {'function': self.function, 66 | 'description': self.description} 67 | else: 68 | return _('%(description)s') % \ 69 | {'description': self.description} 70 | -------------------------------------------------------------------------------- /src/advisor/reports/report_mp.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright 2017-2018 Arm Ltd. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | SPDX-License-Identifier: Apache-2.0 17 | """ 18 | 19 | import os 20 | from .remarks.files_scanned_remark import FilesScannedRemark 21 | from .remarks.no_issues_found_remark import NoIssuesFoundRemark 22 | from .report_item import ReportItem 23 | from multiprocessing import Pool, Manager 24 | 25 | class Report: 26 | def __init__(self, root_directory, target_os='linux'): 27 | manager = Manager() 28 | self.issues = manager.list() 29 | self.errors = manager.list() 30 | self.remarks = manager.list() 31 | self.root_directory = root_directory 32 | self.source_files = manager.list() 33 | self.source_dirs = set() 34 | self.target_os = target_os 35 | self.open_text_mode = 'w' 36 | self.send_filename = False 37 | self.self_process = False 38 | 39 | def add_source_file(self, source_file): 40 | self.source_files.append(source_file) 41 | self.source_dirs.add(os.path.dirname(source_file)) 42 | 43 | def add_issue(self, item): 44 | self.issues.append(item) 45 | 46 | def add_remark(self, item): 47 | self.remarks.append(item) 48 | 49 | def add_error(self, error): 50 | self.errors.append(error) 51 | 52 | def write(self, output_file, report_errors=False, report_remarks=False, include_summary=False): 53 | items = {} 54 | for item_type in ReportItem.TYPES: 55 | items[item_type] = [] 56 | all_items = [] 57 | if report_remarks: 58 | all_items += self.remarks 59 | all_items += self.issues 60 | if report_errors: 61 | all_items += self.errors 62 | for item in all_items: 63 | items[item.item_type].append(item) 64 | if include_summary: 65 | items[ReportItem.SUMMARY].append( 66 | FilesScannedRemark(len(self.source_files))) 67 | if not items[ReportItem.NEGATIVE] and not items[ReportItem.NEUTRAL] and report_remarks: 68 | items[ReportItem.POSITIVE].append(NoIssuesFoundRemark()) 69 | sorted_items = [] 70 | for item_type in ReportItem.TYPES: 71 | sorted_items += sorted(items[item_type], key=lambda item: ( 72 | (item.filename if item.filename else '') + ':' + item.description)) 73 | self.write_items(output_file, sorted_items) 74 | 75 | def write_items(self, output_file, items): 76 | for item in items: 77 | print(item, file=output_file) 78 | -------------------------------------------------------------------------------- /src/advisor/reports/text_report.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright 2020 Arm Ltd. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | SPDX-License-Identifier: Apache-2.0 17 | """ 18 | 19 | from .report import Report 20 | 21 | class TextReport(Report): 22 | """Generates a text report.""" 23 | 24 | def write(self, output_file, report_errors=True, report_remarks=True, include_summary=True): 25 | """ Override write to report all items.""" 26 | super().write(output_file, report_errors=report_errors, 27 | report_remarks=report_remarks, include_summary=include_summary) 28 | -------------------------------------------------------------------------------- /src/advisor/rules/csharp.json: -------------------------------------------------------------------------------- 1 | { 2 | "languageRules": { 3 | "name": "C#", 4 | "extensions": [".cs"], 5 | "dependencyFiles": ["*.csproj"] 6 | }, 7 | "toolsRules": [], 8 | "libraryRules": [] 9 | } -------------------------------------------------------------------------------- /src/advisor/rules/go.json: -------------------------------------------------------------------------------- 1 | { 2 | "languageRules": { 3 | "name": "Go", 4 | "detailsUrl": "https://github.com/AmpereComputing/ampere-porting-advisor/blob/main/doc/golang.md", 5 | "extensions": [".go"], 6 | "dependencyFiles": ["go.mod"], 7 | "minVersion": "1.16", 8 | "recommendedVersion": "1.18" 9 | }, 10 | "libraryRules": [ 11 | { "name": "github.com/golang/snappy", "minVersion": "0.0.2" }, 12 | { "name": "github.com/argoproj/argo-cd/v2", "minVersion": "2.4.0" } 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /src/advisor/rules/java.json: -------------------------------------------------------------------------------- 1 | { 2 | "languageRules": { 3 | "name": "Java", 4 | "detailsUrl": "https://github.com/AmpereComputing/ampere-porting-advisor/blob/main/doc/java.md", 5 | "extensions": [".java", ".jar", ".war"], 6 | "dependencyFiles": ["pom.xml"], 7 | "minVersion": "8", 8 | "recommendedVersion": "17" 9 | }, 10 | "toolsRules": [ 11 | { 12 | "name": "OpenJDK", 13 | "details": "detected java code. we recommend using OpenJDK version 17 or above release for aarch64.", 14 | "detailsUrl": "https://github.com/AmpereComputing/ampere-porting-advisor/blob/main/doc/java.md#prebuilt-java-release" 15 | } 16 | ], 17 | "libraryRules": [ 18 | { "name": "commons-crypto", "minVersion": "1.1.0" }, 19 | { "name": "Fingagle", "minVersion": "20.12.0" }, 20 | { "name": "hadoop-lzo", "specialInstructions": "this library requires a manual build", "detailsUrl": "https://github.com/AmpereComputing/ampere-porting-advisor/blob/main/doc/java.md#building-jar-libraries-manually" }, 21 | { "name": "jansi-native", "specialInstructions": "this library requires a manual build", "detailsUrl": "https://github.com/AmpereComputing/ampere-porting-advisor/blob/main/doc/java.md#building-jar-libraries-manually" }, 22 | { "name": "jffi", "minVersion": "1.2.13" }, 23 | { "name": "leveldbjni-all", "unsupported": true }, 24 | { "name": "lz4-java", "minVersion": "1.4.0" }, 25 | { "name": "netty-transport-native-epoll", "minVersion": "4.1.50" }, 26 | { "name": "netty-tcname", "minVersion": "2.0.31" }, 27 | { "name": "rockdbjni", "minVersion": "5.0.1" }, 28 | { "name": "sigar", "specialInstructions": "this library requires a manual build", "detailsUrl": "https://github.com/AmpereComputing/ampere-porting-advisor/blob/main/doc/java.md#building-jar-libraries-manually" }, 29 | { "name": "snappy-java", "minVersion": "1.1.4" }, 30 | { "name": "zstd-jni", "minVersion": "1.2.0" } 31 | ] 32 | } 33 | -------------------------------------------------------------------------------- /src/advisor/rules/node.json: -------------------------------------------------------------------------------- 1 | { 2 | "languageRules": { 3 | "name": "Node.js", 4 | "extensions": [".js"], 5 | "dependencyFiles": ["package.json"] 6 | }, 7 | "toolsRules": [], 8 | "libraryRules": [] 9 | } -------------------------------------------------------------------------------- /src/advisor/rules/ruby.json: -------------------------------------------------------------------------------- 1 | { 2 | "languageRules": { 3 | "name": "Ruby", 4 | "extensions": [".rb"], 5 | "dependencyFiles": ["Gemfile"] 6 | }, 7 | "toolsRules": [], 8 | "libraryRules": [] 9 | } -------------------------------------------------------------------------------- /src/advisor/rules/sample.json: -------------------------------------------------------------------------------- 1 | { 2 | "languageRules": [ 3 | { 4 | "name": "Language", 5 | "minVersion": "1.2.3", 6 | "recommendedVersion": "2.3.4", 7 | "extensions": [".py"], 8 | "dependencyFiles": ["requirements.txt"], 9 | "description": "Additional Instructions", 10 | "detailsUrl": "https://amperecomputing.com/developers", 11 | "overrideText": "Only this will show up" 12 | } 13 | ], 14 | "toolsRules": [ 15 | { 16 | "name": "Tool", 17 | "minVersion": "1.2.3", 18 | "recommendedVersion": "2.3.4", 19 | "description": "Additional Instructions", 20 | "detailsUrl": "https://amperecomputing.com/developers", 21 | "overrideText": "Only this will show up" 22 | } 23 | ], 24 | "libraryRules": [ 25 | { 26 | "name": "name", 27 | "minVersion": "1.2.3", 28 | "recommendedVersion": "2.3.4", 29 | "description": "Additional Instructions", 30 | "detailsUrl": "https://amperecomputing.com/developers", 31 | "overrideText": "Only this will show up" 32 | }, 33 | { 34 | "name": "special-library", 35 | "specialInstructions": "this library requires a manual build", 36 | "detailsUrl": "https://howtobuildthislibrarymanually.com" 37 | }, 38 | { 39 | "name": "unsupported-library", 40 | "unsupported": true 41 | } 42 | ] 43 | } 44 | -------------------------------------------------------------------------------- /src/advisor/scanners/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AmpereComputing/ampere-porting-advisor/510aa1345d4ac2c61e9fc3b0ab145a1ba5178dd6/src/advisor/scanners/__init__.py -------------------------------------------------------------------------------- /src/advisor/scanners/asm_source_scanner.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright 2017 Arm Ltd. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | SPDX-License-Identifier: Apache-2.0 17 | """ 18 | 19 | import os 20 | import re 21 | from ..reports.issues.asm_source_issue import AsmSourceIssue 22 | from .scanner import Scanner 23 | 24 | 25 | class AsmSourceScanner(Scanner): 26 | """Scanner that looks for assembly source files.""" 27 | 28 | ASM_SOURCE_EXTENSIONS = ['.s'] 29 | 30 | INSTRUCTION_RE_PROG = re.compile('%[re][a-z]+|r[0-9],|^[a-z][ \t]+[0-9]') 31 | """Some assembly source files don't actually contain any architecture 32 | specific instructions. This is designed to match common 33 | instruction syntaxes.""" 34 | 35 | def accepts_file(self, filename): 36 | _, ext = os.path.splitext(filename) 37 | return ext.lower() in AsmSourceScanner.ASM_SOURCE_EXTENSIONS 38 | 39 | def scan_file_object(self, filename, file, report): 40 | for line in file: 41 | if AsmSourceScanner.INSTRUCTION_RE_PROG.search(line): 42 | report.add_issue(AsmSourceIssue(filename)) 43 | break 44 | -------------------------------------------------------------------------------- /src/advisor/scanners/auto_scanner.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright 2017 Arm Ltd. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | SPDX-License-Identifier: Apache-2.0 17 | """ 18 | 19 | from .scanner import Scanner 20 | 21 | 22 | class AutoScanner(Scanner): 23 | """Scanner that automatically scans a file using one of a set of scanners, 24 | based on which scanner accepts the file.""" 25 | 26 | def __init__(self, scanners): 27 | super().__init__() 28 | self.scanners = scanners 29 | 30 | def accepts_file(self, path): 31 | for scanner in self.scanners: 32 | if scanner.accepts_file(path): 33 | return True 34 | return False 35 | 36 | def scan_file(self, path, report): 37 | for scanner in self.scanners: 38 | if scanner.accepts_file(path): 39 | scanner.scan_file(path, report) 40 | -------------------------------------------------------------------------------- /src/advisor/scanners/config_guess_scanner.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright 2017 Arm Ltd. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | SPDX-License-Identifier: Apache-2.0 17 | """ 18 | 19 | import os 20 | from ..reports.issues.config_guess_issue import ConfigGuessIssue 21 | from ..reports.remarks.config_guess_remark import ConfigGuessRemark 22 | from .scanner import Scanner 23 | 24 | 25 | class ConfigGuessScanner(Scanner): 26 | """Scanner that scans config.guess files for aarch64 support.""" 27 | 28 | def accepts_file(self, filename): 29 | return os.path.basename(filename) == 'config.guess' 30 | 31 | def scan_file_object(self, filename, file, report): 32 | for line in file: 33 | if 'aarch64:Linux' in line: 34 | report.add_remark(ConfigGuessRemark(filename=filename)) 35 | break 36 | else: 37 | report.add_issue(ConfigGuessIssue(filename=filename)) 38 | -------------------------------------------------------------------------------- /src/advisor/scanners/go_scanner.py: -------------------------------------------------------------------------------- 1 | import os 2 | import re 3 | from ..manifester.manifester import Manifester 4 | from .language_scanner import LanguageScanner 5 | 6 | 7 | class GoScanner(LanguageScanner): 8 | """Scanner that checks Go files for potential porting issues.""" 9 | 10 | def __init__(self) -> None: 11 | self.LANGUAGE_NAME = 'go' 12 | self.LANGUAGE_VERSION_RE = r'^go (?P[\w\.]*)$' 13 | super().__init__() 14 | 15 | def scan_file_object(self, filename, file_object, report): 16 | """Scans the provided file and adds issues, remarks or errors as needed to the report. 17 | 18 | Args: 19 | filename: The name of the file being checked. 20 | file_object: The file contents. 21 | report: The report being generated. 22 | """ 23 | if os.path.basename(filename) in self.DEPENDENCY_FILES: 24 | file_contents = file_object.read() 25 | go_match = re.findall(self.LANGUAGE_VERSION_RE, file_contents, re.MULTILINE) 26 | if go_match and go_match[0]: 27 | self.INSTALLED_VERSION = go_match[0] 28 | self.add_language_remarks(report) 29 | 30 | manifester = Manifester() 31 | dependencies = manifester.get_dependencies(filename, file_object, report) 32 | 33 | self.add_library_remarks(dependencies, report) 34 | -------------------------------------------------------------------------------- /src/advisor/scanners/java_scanner.py: -------------------------------------------------------------------------------- 1 | import os 2 | from os import path 3 | from ..helpers.java.java_tool_invoker import JavaToolInvoker 4 | from ..helpers.utils import Utils 5 | from ..manifester.manifester import Manifester 6 | from ..reports.issues.native_methods_issue import NativeMethodsIssue 7 | from ..reports.report_item import ReportItem 8 | from .language_scanner import LanguageScanner 9 | 10 | 11 | class JavaScanner(LanguageScanner): 12 | """Scanner that checks Java files for potential porting issues.""" 13 | 14 | def __init__(self) -> None: 15 | self.LANGUAGE_NAME = 'java' 16 | self._added_jar_remark = False 17 | super().__init__() 18 | 19 | def scan_file_object(self, filename, file_object, report): 20 | """Scans the provided file and adds issues, remarks, or errors as needed to the Report. 21 | 22 | Args: 23 | filename: The name of the file being checked. 24 | file_object: The file contents. 25 | report: The report being generated. 26 | """ 27 | if self.has_source_extension(filename): 28 | self._add_java_language_remark(report) 29 | _, ext = os.path.splitext(filename) 30 | tool_invoker = JavaToolInvoker() 31 | is_jar_or_war = ext == '.jar' or ext == '.war' 32 | if is_jar_or_war and tool_invoker.can_run(): 33 | result, message = tool_invoker.ampere_ready_assessor(filename) 34 | if (result == 3): 35 | report.add_issue(NativeMethodsIssue(message, filename=filename)) 36 | elif is_jar_or_war: 37 | self.add_jar_remark(report) 38 | 39 | return 40 | 41 | if path.basename(filename) in self.DEPENDENCY_FILES: 42 | manifester = Manifester() 43 | dependencies = manifester.get_dependencies(filename, file_object, report) 44 | self.add_library_remarks(dependencies, report) 45 | 46 | def _add_java_language_remark(self, report): 47 | """Adds the Java version and Corretto recommendations. 48 | 49 | Args: 50 | report: The report to add the remark to. 51 | """ 52 | self.add_language_remarks(report) 53 | 54 | def add_jar_remark(self, report): 55 | if not self._added_jar_remark: 56 | if Utils.running_from_binary(): 57 | report.add_remark(ReportItem('java is not installed. ' 58 | 'We need java to scan jar files for native methods')) 59 | else: 60 | # running from script, both java and Maven are needed 61 | report.add_remark(ReportItem('java and/or Maven are not installed. ' 62 | 'We need java and Maven to scan jar files for native methods')) 63 | self._added_jar_remark = True 64 | -------------------------------------------------------------------------------- /src/advisor/scanners/python_scanner.py: -------------------------------------------------------------------------------- 1 | from os import path 2 | from ..helpers.python.python_version_checker import PythonVersionChecker 3 | from ..manifester.manifester import Manifester 4 | from .language_scanner import LanguageScanner 5 | 6 | 7 | class PythonScanner(LanguageScanner): 8 | """Scanner that scans Python source files for potential porting issues.""" 9 | 10 | def __init__(self) -> None: 11 | self.LANGUAGE_NAME = 'python' 12 | super().__init__() 13 | 14 | def scan_file_object(self, filename, file_object, report): 15 | """Scans the provided file and adds issues, remarks, or errors as needed to the Report. 16 | 17 | Args: 18 | filename: The name of the file being checked. 19 | file_object: The file contents. 20 | report: The report being generated. 21 | """ 22 | if not self._added_language_version_remark and self.has_source_extension(filename): 23 | self._add_python_language_remark(report) 24 | return 25 | 26 | if path.basename(filename) in self.DEPENDENCY_FILES: 27 | manifester = Manifester() 28 | dependencies = manifester.get_dependencies(filename, file_object, report) 29 | self.add_library_remarks(dependencies, report) 30 | 31 | def _add_python_language_remark(self, report): 32 | """Adds the Python version recommendations. 33 | 34 | Args: 35 | report: The report to add the remark to. 36 | """ 37 | self.INSTALLED_VERSION = PythonVersionChecker.get_python_version() 38 | installed_pip_version = PythonVersionChecker.get_pip_version() 39 | 40 | self.TOOLS_RULES['pip']['installedVersion'] = installed_pip_version 41 | 42 | self.add_language_remarks(report) 43 | -------------------------------------------------------------------------------- /src/advisor/scanners/scanners.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright 2017-2018 Arm Ltd. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | SPDX-License-Identifier: Apache-2.0 17 | """ 18 | 19 | from ..filters.issue_type_filter import IssueTypeFilter 20 | from ..filters.other_issues_filter import OtherIssuesFilter 21 | from ..filters.port_filter import PortFilter 22 | from ..filters.target_os_filter import TargetOsFilter 23 | from .asm_source_scanner import AsmSourceScanner 24 | from .config_guess_scanner import ConfigGuessScanner 25 | from .go_scanner import GoScanner 26 | from .java_scanner import JavaScanner 27 | from .makefile_scanner import MakefileScanner 28 | from .cmake_scanner import CMakeScanner 29 | from .meson_scanner import MesonScanner 30 | from .python_scanner import PythonScanner 31 | from .source_scanner import SourceScanner 32 | 33 | 34 | class Scanners: 35 | """Set of scanners that may be used to scan for potential porting issues in 36 | files.""" 37 | 38 | def __init__(self, issue_type_config, filter_ported_code=True): 39 | """Initializes the set of scanners that may be used to scan for 40 | potential porting issues in files. 41 | 42 | Args: 43 | issue_type_config (IssueTypeConfig): issue type filter 44 | configuration. 45 | """ 46 | self.scanners = [SourceScanner(filter_ported_code=filter_ported_code), 47 | PythonScanner(), 48 | JavaScanner(), 49 | GoScanner(), 50 | AsmSourceScanner(), 51 | ConfigGuessScanner(), 52 | MakefileScanner(), 53 | CMakeScanner(), 54 | MesonScanner()] 55 | self.filters = [PortFilter()] if filter_ported_code else [] 56 | self.filters += [IssueTypeFilter(issue_type_config), 57 | TargetOsFilter(), 58 | OtherIssuesFilter()] 59 | 60 | def initialize_report(self, report): 61 | """Initializes the given report for scanning. 62 | 63 | Args: 64 | report (Report): Report to initialize_report. 65 | """ 66 | for a_filter in self.filters: 67 | a_filter.initialize_report(report) 68 | for scanner in self.scanners: 69 | scanner.initialize_report(report) 70 | 71 | def finalize_report(self, report): 72 | """Finalizes the given report. 73 | 74 | Args: 75 | report (Report): Report to finalize_report. 76 | """ 77 | for scanner in self.scanners: 78 | scanner.finalize_report(report) 79 | for a_filter in self.filters: 80 | a_filter.finalize_report(report) 81 | 82 | def __iter__(self): 83 | return self.scanners.__iter__() 84 | -------------------------------------------------------------------------------- /src/advisor/templates/status-info.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 14 | 15 | 16 | 17 | 18 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /src/advisor/templates/status-negative.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 14 | 15 | 16 | 17 | 18 | 21 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /src/advisor/templates/status-positive.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 14 | 15 | 16 | 17 | 18 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /src/advisor/templates/status-warning.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 14 | 15 | 16 | 17 | 18 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /src/advisor/tools/ampere-ready-java/.gitignore: -------------------------------------------------------------------------------- 1 | dependency-reduced-pom.xml 2 | target/ 3 | *.jar 4 | .idea/ -------------------------------------------------------------------------------- /src/advisor/tools/ampere-ready-java/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2023 Ampere Computing or its affiliates. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this 4 | software and associated documentation files (the "Software"), to deal in the Software 5 | without restriction, including without limitation the rights to use, copy, modify, 6 | merge, publish, distribute, sublicense, and/or sell copies of the Software, and to 7 | permit persons to whom the Software is furnished to do so. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 10 | INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 11 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 12 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 13 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 14 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | -------------------------------------------------------------------------------- /src/advisor/tools/ampere-ready-java/README.md: -------------------------------------------------------------------------------- 1 | Ampere-Ready Assessor for Java 2 | ==================================== 3 | 4 | This is a fork of [AWS Graviton-Ready Assessor](https://github.com/aws/porting-advisor-for-graviton/tree/main/src/advisor/tools/graviton-ready-java), an open source Java application contributed by [Michael Fischer](https://github.com/otterley). This fork has updated terminologies to Ampere equivalents. 5 | 6 | This application can help you determine whether your Java application is ready 7 | to run on bare-metal or VM instances powered by Ampere Processors. More 8 | information about Ampere Cloud Native Processors can be found 9 | [here](https://amperecomputing.com/products/processors). 10 | 11 | Many Java applications are ready to run on Ampere Processors without modification. In 12 | particular, pure Java applications that do not use Java Native Interface (JNI) 13 | will often run seamlessly with no changes at all. Many third-party and Open 14 | Source applications that have native libraries will also run without 15 | modification, if they ship with those native libraries for the aarch64 16 | architecture on Linux. 17 | 18 | To determine whether your application is ready to run on Ampere Processors, simply run 19 | this application and point it at your JAR or WAR file, or a folder that contains 20 | your JAR and/or WAR files. If your application is "clean" (i.e., it has no 21 | native libraries, or all native libraries are available for aarch64 on Linux), 22 | it will tell you. If there are native libraries missing, it will try to inform 23 | you of the actions you can take to make your application or its dependencies 24 | compatible with Ampere Processors. 25 | -------------------------------------------------------------------------------- /src/advisor/tools/ampere-ready-java/src/main/java/com/ampere/labs/GravitonReadyAssessor/ClassInfo.java: -------------------------------------------------------------------------------- 1 | package com.ampere.labs.AmpereReadyAssessor; 2 | 3 | import java.net.URL; 4 | import java.util.Date; 5 | 6 | import com.fasterxml.jackson.annotation.JsonFormat; 7 | import com.fasterxml.jackson.databind.annotation.JsonSerialize; 8 | import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; 9 | import lombok.Builder; 10 | import lombok.Data; 11 | import lombok.extern.jackson.Jacksonized; 12 | import org.osgi.framework.VersionRange; 13 | 14 | @Data 15 | @Builder 16 | @Jacksonized 17 | public class ClassInfo { 18 | private String implementationTitle; 19 | private String implementationVendor; 20 | 21 | @JsonSerialize(using = ToStringSerializer.class) 22 | private VersionRange implementationVersionRange; 23 | 24 | private String specificationTitle; 25 | private String specificationVendor; 26 | 27 | @JsonSerialize(using = ToStringSerializer.class) 28 | private VersionRange specificationVersionRange; 29 | 30 | private String status; 31 | private String description; 32 | private URL url; 33 | 34 | @JsonFormat(shape = JsonFormat.Shape.STRING) 35 | private Date lastUpdated; 36 | } 37 | -------------------------------------------------------------------------------- /src/advisor/tools/ampere-ready-java/src/main/java/com/ampere/labs/GravitonReadyAssessor/Config.java: -------------------------------------------------------------------------------- 1 | package com.ampere.labs.AmpereReadyAssessor; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import com.fasterxml.jackson.core.JsonProcessingException; 5 | import com.fasterxml.jackson.databind.JsonMappingException; 6 | import com.fasterxml.jackson.databind.ObjectMapper; 7 | import lombok.Builder; 8 | import lombok.Data; 9 | import lombok.Singular; 10 | import lombok.extern.jackson.Jacksonized; 11 | 12 | import java.io.IOException; 13 | import java.net.URL; 14 | import java.util.List; 15 | 16 | @Data 17 | @Builder 18 | @Jacksonized 19 | public class Config { 20 | @JsonProperty("classes") 21 | @Singular public List classInfos; 22 | 23 | public static Config fromURL(URL url) throws IOException { 24 | ObjectMapper mapper = new ObjectMapper(); 25 | return mapper.readerFor(Config.class).readValue(url); 26 | } 27 | 28 | public static Config fromJson(String s) throws JsonProcessingException, JsonMappingException { 29 | ObjectMapper mapper = new ObjectMapper(); 30 | return mapper.readerFor(Config.class).readValue(s); 31 | } 32 | 33 | public String toJson() throws JsonProcessingException { 34 | ObjectMapper mapper = new ObjectMapper(); 35 | return mapper.writerWithDefaultPrettyPrinter().writeValueAsString(this); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/advisor/tools/ampere-ready-java/src/main/java/com/ampere/labs/GravitonReadyAssessor/JarChecker.java: -------------------------------------------------------------------------------- 1 | package com.ampere.labs.AmpereReadyAssessor; 2 | 3 | public abstract class JarChecker implements JarCheckerInterface { 4 | } 5 | -------------------------------------------------------------------------------- /src/advisor/tools/ampere-ready-java/src/main/java/com/ampere/labs/GravitonReadyAssessor/JarCheckerInterface.java: -------------------------------------------------------------------------------- 1 | package com.ampere.labs.AmpereReadyAssessor; 2 | 3 | import java.io.IOException; 4 | import java.util.List; 5 | 6 | public interface JarCheckerInterface { 7 | List getSharedLibraryPaths() throws IOException; 8 | } 9 | -------------------------------------------------------------------------------- /src/advisor/tools/ampere-ready-java/src/main/java/com/ampere/labs/GravitonReadyAssessor/JarFileScanner.java: -------------------------------------------------------------------------------- 1 | package com.ampere.labs.AmpereReadyAssessor; 2 | 3 | import lombok.NonNull; 4 | import lombok.RequiredArgsConstructor; 5 | 6 | import java.io.IOException; 7 | import java.util.ArrayList; 8 | import java.util.Enumeration; 9 | import java.util.List; 10 | import java.util.jar.JarEntry; 11 | import java.util.jar.JarFile; 12 | import java.util.logging.Logger; 13 | 14 | /** 15 | * A JAR file scanner that locates native code libraries via simple 16 | * path-matching. If the file ends in .so and has the strings "aarch64" 17 | * and "linux", it is considered a match. 18 | */ 19 | @RequiredArgsConstructor 20 | public class JarFileScanner extends JarChecker { 21 | Logger logger = SimpleLogger.getLogger(); 22 | 23 | @NonNull 24 | private JarFile jarFile; 25 | 26 | /** 27 | * Return a list of path names corresponding to shared library files 28 | * in the JAR file. 29 | * 30 | * @return list of shared library pathnames 31 | * @throws IOException 32 | */ 33 | public List getSharedLibraryPaths() throws IOException { 34 | final List sharedLibraryPaths = new ArrayList<>(); 35 | final Enumeration entries = jarFile.entries(); 36 | 37 | while (entries.hasMoreElements()) { 38 | final JarEntry entry = entries.nextElement(); 39 | final String entryName = entry.getName(); 40 | 41 | if (!entry.isDirectory() && 42 | entryName.endsWith(".so") && 43 | entryName.toLowerCase().contains("aarch64") && 44 | entryName.toLowerCase().contains("linux")) 45 | sharedLibraryPaths.add(entryName); 46 | } 47 | return sharedLibraryPaths; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/advisor/tools/ampere-ready-java/src/main/java/com/ampere/labs/GravitonReadyAssessor/JarManifestScanner.java: -------------------------------------------------------------------------------- 1 | package com.ampere.labs.AmpereReadyAssessor; 2 | 3 | import lombok.NonNull; 4 | import lombok.RequiredArgsConstructor; 5 | 6 | import java.io.IOException; 7 | import java.util.List; 8 | import java.util.jar.JarFile; 9 | import java.util.stream.Collectors; 10 | 11 | /** 12 | * A JAR file scanner that locates native code libraries by looking at 13 | * the JAR's manifest. It uses the OSGI Bundle-NativeCode 14 | * attribute for this purpose. 15 | */ 16 | @RequiredArgsConstructor 17 | public class JarManifestScanner extends JarChecker { 18 | @NonNull 19 | private JarFile jarFile; 20 | 21 | /** 22 | * Return a list of path names corresponding to shared library files 23 | * in the JAR file. 24 | * 25 | * @return list of shared library pathnames 26 | * @throws IOException 27 | */ 28 | public List getSharedLibraryPaths() throws IOException { 29 | NativeCodeManifest manifest = NativeCodeManifest.fromJarFile(this.jarFile); 30 | 31 | // No native code manifest found 32 | if (manifest == null) return List.of(); 33 | 34 | return manifest.getRecords().stream() 35 | .filter(NativeCodeManifestRecord::isAarch64) 36 | .filter(NativeCodeManifestRecord::isLinux) 37 | .map(NativeCodeManifestRecord::getLibpath) 38 | .collect(Collectors.toList()); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/advisor/tools/ampere-ready-java/src/main/java/com/ampere/labs/GravitonReadyAssessor/NativeCodeManifest.java: -------------------------------------------------------------------------------- 1 | package com.ampere.labs.AmpereReadyAssessor; 2 | 3 | import lombok.Getter; 4 | import lombok.NonNull; 5 | 6 | import java.io.IOException; 7 | import java.util.Arrays; 8 | import java.util.jar.Attributes; 9 | import java.util.jar.JarFile; 10 | import java.util.jar.Manifest; 11 | import java.util.List; 12 | import java.util.stream.Collectors; 13 | 14 | /** 15 | *

A native code bundle JAR manifest entry.

16 | * 17 | *

JAR files have 18 | * manifests 19 | * in them which contain various metadata in them. These metadata are known as 20 | * attributes. Some 21 | * JAR files have a Bundle-NativeCode attribute in them that indicates where native code 22 | * can be found. The format of this attribute's value is defined by the OSGI Framework and is 23 | * documented here.

24 | */ 25 | public class NativeCodeManifest { 26 | final static String BundleNativeCode = "Bundle-NativeCode"; 27 | 28 | @Getter 29 | private List records; 30 | 31 | /** 32 | * Constructs a NativeCodeManifest from a JarFile object. 33 | * @param jarFile the JarFile 34 | * @return the NativeCodeManifest 35 | * @throws IOException 36 | */ 37 | public static NativeCodeManifest fromJarFile(@NonNull JarFile jarFile) throws IOException { 38 | Manifest manifest = jarFile.getManifest(); 39 | Attributes attrs = manifest.getMainAttributes(); 40 | String bundleNativeCode = attrs.getValue(BundleNativeCode); 41 | 42 | if (bundleNativeCode == null) return null; 43 | 44 | return fromString(bundleNativeCode); 45 | } 46 | 47 | /** 48 | * Constructs a NativeCodeManifest from a JarFile object. 49 | * @param attributeValue the value of the Bundle-NativeCode Manifest attribute 50 | * @return the NativeCodeManifest 51 | */ 52 | private static NativeCodeManifest fromString(@NonNull String attributeValue) { 53 | NativeCodeManifest manifest = new NativeCodeManifest(); 54 | 55 | // Records are separated by `,` 56 | manifest.records = Arrays.stream(attributeValue.split(",")) 57 | .map(String::trim) 58 | .map(NativeCodeManifestRecord::fromString) 59 | .collect(Collectors.toUnmodifiableList()); 60 | return manifest; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/advisor/tools/ampere-ready-java/src/main/java/com/ampere/labs/GravitonReadyAssessor/NativeCodeManifestRecord.java: -------------------------------------------------------------------------------- 1 | package com.ampere.labs.AmpereReadyAssessor; 2 | 3 | import lombok.Getter; 4 | import lombok.NonNull; 5 | import lombok.Setter; 6 | 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | 10 | /** 11 | * A record in a Bundle-NativeCode JAR manifest attribute. 12 | */ 13 | public class NativeCodeManifestRecord { 14 | @Getter 15 | @Setter 16 | private String libpath; 17 | 18 | private final List osnames = new ArrayList<>(); 19 | private final List arches = new ArrayList<>(); 20 | 21 | /** 22 | * Creates a NativeCodeManifestRecord from its string representation. 23 | * @param text The raw text 24 | * @return a NativeCodeManifestRecord 25 | */ 26 | public static NativeCodeManifestRecord fromString(@NonNull String text) { 27 | NativeCodeManifestRecord entry = new NativeCodeManifestRecord(); 28 | List kvPairs = List.of(text.split(";")); 29 | entry.setLibpath(kvPairs.get(0)); 30 | // Record any processor architectures or OS names found within 31 | kvPairs.stream().skip(1).forEach(pair -> { 32 | String key = pair.split("=")[0]; 33 | String val = pair.split("=")[1]; 34 | if (key.equals("osname")) { 35 | entry.addOSName(val); 36 | } 37 | if (key.equals("processor")) { 38 | entry.addArch(val); 39 | } 40 | }); 41 | return entry; 42 | } 43 | 44 | public void addOSName(String osName) { 45 | osnames.add(osName); 46 | } 47 | 48 | public void addArch(String arch) { 49 | arches.add(arch); 50 | } 51 | 52 | public boolean isLinux() { 53 | return osnames.stream().anyMatch(name -> name.equalsIgnoreCase("linux")); 54 | } 55 | 56 | public boolean isAarch64() { 57 | return arches.stream().anyMatch(name -> name.equalsIgnoreCase("aarch64")); 58 | } 59 | 60 | @Override 61 | public String toString() { 62 | return "libpath: " + libpath + "; arches=" + this.arches + "; osnames=" + this.osnames; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/advisor/tools/ampere-ready-java/src/main/java/com/ampere/labs/GravitonReadyAssessor/SimpleLogger.java: -------------------------------------------------------------------------------- 1 | package com.ampere.labs.AmpereReadyAssessor; 2 | 3 | import lombok.NonNull; 4 | 5 | import java.io.OutputStream; 6 | import java.util.Properties; 7 | import java.util.logging.*; 8 | 9 | /** 10 | * A simple unbuffered logger that simply prints each log line as-is to standard output (System.out). 11 | */ 12 | public class SimpleLogger { 13 | private static Logger logger; 14 | private static Handler handler; 15 | 16 | static { 17 | Properties logProps = System.getProperties(); 18 | logProps.setProperty("java.util.logging.SimpleFormatter.format", "%5$s%n"); 19 | System.setProperties(logProps); 20 | } 21 | 22 | /** 23 | * Obtain the singleton Logger instance. 24 | * 25 | * @return The logger instance 26 | * @throws SecurityException 27 | */ 28 | public static Logger getLogger() throws SecurityException { 29 | if (logger != null) { 30 | return logger; 31 | } 32 | logger = Logger.getLogger(SimpleLogger.class.toString()); 33 | logger.setUseParentHandlers(false); 34 | handler = getAutoFlushingStreamHandler(System.out, new SimpleFormatter()); 35 | logger.addHandler(handler); 36 | return logger; 37 | } 38 | 39 | /** 40 | * Sets the lowest log level that this logger will emit. Logs with a level lower than 41 | * this will be omitted from the output. 42 | * 43 | * @param level The log level 44 | */ 45 | public static void setLevel(@NonNull Level level) { 46 | if (logger == null) getLogger(); 47 | handler.setLevel(level); 48 | logger.setLevel(level); 49 | } 50 | 51 | /** 52 | * Returns a StreamHandler that flushes after every publish() invocation. 53 | * @param o the OutputStream passed to the StreamHandler constructor 54 | * @param f the Formatter passed to the StreamHandler constructor 55 | * @return 56 | */ 57 | private static StreamHandler getAutoFlushingStreamHandler(@NonNull OutputStream o, @NonNull Formatter f) { 58 | return new StreamHandler(o, f) { 59 | @Override 60 | public synchronized void publish(@NonNull final LogRecord record) { 61 | super.publish(record); 62 | flush(); 63 | } 64 | }; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/advisor/tools/ampere-ready-java/src/test/files/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "classes" : [ { 3 | "implementationTitle" : "ImplementationTitle", 4 | "implementationVendor" : "ImplementationVendor", 5 | "implementationVersionRange" : "[1.0.0,2.0.0)", 6 | "specificationTitle" : "SpecificationTitle", 7 | "specificationVendor" : "SpecificationVendor", 8 | "specificationVersionRange" : "[1.0.0,2.0.0)", 9 | "status" : "OK", 10 | "description" : "Description goes here", 11 | "url" : "http://example.com", 12 | "lastUpdated" : "2022-01-22T00:50:03.114+00:00" 13 | } ] 14 | } -------------------------------------------------------------------------------- /src/advisor/tools/ampere-ready-java/src/test/java/com/ampere/labs/GravitonReadyAssessor/AppTest.java: -------------------------------------------------------------------------------- 1 | package com.ampere.labs.AmpereReadyAssessor; 2 | 3 | import static org.junit.Assert.assertTrue; 4 | 5 | import org.junit.Test; 6 | 7 | /** 8 | * Unit test for simple App. 9 | */ 10 | public class AppTest 11 | { 12 | /** 13 | * Rigorous Test :-) 14 | */ 15 | @Test 16 | public void shouldAnswerWithTrue() 17 | { 18 | assertTrue( true ); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/advisor/tools/ampere-ready-java/src/test/java/com/ampere/labs/GravitonReadyAssessor/ConfigFileTest.java: -------------------------------------------------------------------------------- 1 | package com.ampere.labs.AmpereReadyAssessor; 2 | 3 | import com.fasterxml.jackson.core.JsonProcessingException; 4 | 5 | import org.junit.Test; 6 | 7 | import static org.junit.Assert.*; 8 | 9 | import org.osgi.framework.Version; 10 | import org.osgi.framework.VersionRange; 11 | 12 | import java.io.IOException; 13 | import java.net.MalformedURLException; 14 | import java.net.URL; 15 | import java.util.Date; 16 | 17 | public class ConfigFileTest { 18 | @Test 19 | public void shouldLoadConfigFile() { 20 | try { 21 | URL url = new URL("file:src/test/files/config.json"); 22 | Config c = Config.fromURL(url); 23 | System.out.println(c); 24 | } catch (IOException e) { 25 | fail(e.toString()); 26 | } 27 | } 28 | 29 | @Test 30 | public void shouldPrintJSON() { 31 | try { 32 | Config c = generateConfig(); 33 | System.out.println(c.toJson()); 34 | } catch(JsonProcessingException e) { 35 | fail(e.toString()); 36 | } 37 | } 38 | 39 | @Test 40 | public void shouldSerializeDeserialize() { 41 | try { 42 | Config c1 = generateConfig(); 43 | String json = c1.toJson(); 44 | Config c2 = Config.fromJson(json); 45 | assertEquals(c1, c2); 46 | } catch(JsonProcessingException e) { 47 | fail(e.toString()); 48 | } 49 | } 50 | 51 | @Test 52 | public void versionInRange() { 53 | Config config = generateConfig(); 54 | assert(config.getClassInfos().size() == 1); 55 | ClassInfo info = config.getClassInfos().get(0); 56 | // TODO 57 | return; 58 | } 59 | 60 | private Config generateConfig() { 61 | try { 62 | ClassInfo i = ClassInfo.builder() 63 | .implementationTitle("ImplementationTitle") 64 | .implementationVendor("ImplementationVendor") 65 | .implementationVersionRange( 66 | new VersionRange( 67 | VersionRange.LEFT_CLOSED, new Version(1, 0, 0), 68 | new Version(2, 0, 0), VersionRange.RIGHT_OPEN) 69 | ) 70 | .specificationTitle("SpecificationTitle") 71 | .specificationVendor("SpecificationVendor") 72 | .specificationVersionRange( 73 | new VersionRange( 74 | VersionRange.LEFT_CLOSED, new Version(1, 0, 0), 75 | new Version(2, 0, 0), VersionRange.RIGHT_OPEN) 76 | ) 77 | .description("Description goes here") 78 | .status("OK") 79 | .url(new URL("http://example.com")) 80 | .lastUpdated(new Date()) 81 | .build(); 82 | return Config.builder() 83 | .classInfo(i) 84 | .build(); 85 | } catch (MalformedURLException e) { 86 | fail(e.toString()); 87 | return null; 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/advisor/tools/ampere-ready-java/target/placeholder.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AmpereComputing/ampere-porting-advisor/510aa1345d4ac2c61e9fc3b0ab145a1ba5178dd6/src/advisor/tools/ampere-ready-java/target/placeholder.md -------------------------------------------------------------------------------- /src/porting-advisor.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | """ 3 | Copyright 2017 Arm Ltd. 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | 17 | SPDX-License-Identifier: Apache-2.0 18 | """ 19 | import sys 20 | 21 | from advisor import main 22 | 23 | if __name__ == '__main__': 24 | if sys.version_info < (3, 10): 25 | print("Python 3.10 or newer is required to run this application.") 26 | sys.exit(1) 27 | main() 28 | -------------------------------------------------------------------------------- /src/updater.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import os 3 | import sys 4 | from advisor import __version__ 5 | from advisor.helpers.version_comparer import VersionComparer 6 | from zipfile import ZipFile 7 | 8 | # Temp test bucket 9 | DOWNLOAD_URL = '' 10 | LATEST_VERSION_URL = '' 11 | 12 | def main(argv=sys.argv[1:]): 13 | check_for_updates() 14 | 15 | 16 | def check_for_updates(): 17 | """Checks for latest version. Displays a message if new message is available. 18 | """ 19 | if (is_newer_version_available()): 20 | print(f'New version of Porting Advisor for Ampere is available. Please download it at: {DOWNLOAD_URL}') 21 | 22 | def is_newer_version_available(): 23 | current_version = __version__ 24 | latest_version = get_latest_version() 25 | return VersionComparer.is_valid(latest_version) and VersionComparer.compare(current_version, latest_version) == -1 26 | 27 | def get_latest_version(): 28 | """Gets latest version available 29 | 30 | Returns: 31 | str: The latest published version. Empty if it failed to get the latest version available. 32 | """ 33 | try: 34 | return do_request(LATEST_VERSION_URL).decode('utf-8') 35 | except: 36 | logging.debug('Error while getting latest version.', exc_info=True) 37 | return '' 38 | 39 | def do_request(request_url): 40 | """Executes an https request 41 | Returns: 42 | bytes: The latest version of the tool. None if it fails. 43 | """ 44 | try: 45 | # if running as a binary, need to specify the path to the cacert.pem for requests to succeed 46 | if getattr(sys, 'frozen', False) and hasattr(sys, '_MEIPASS'): 47 | import certifi.core 48 | certifi.core.where = _get_cacert_pem() 49 | import requests.utils 50 | import requests.adapters 51 | requests.utils.DEFAULT_CA_BUNDLE_PATH = _get_cacert_pem() 52 | requests.adapters.DEFAULT_CA_BUNDLE_PATH = _get_cacert_pem() 53 | else: 54 | import certifi.core 55 | import requests.utils 56 | import requests.adapters 57 | 58 | return requests.get(request_url).content 59 | except: 60 | logging.debug('Error while executing https request.', exc_info=True) 61 | return None 62 | 63 | def _get_cacert_pem(): 64 | return os.path.abspath(os.path.join(os.path.dirname(__file__), 'certifi', 'cacert.pem')) 65 | 66 | if __name__ == '__main__': 67 | main() 68 | -------------------------------------------------------------------------------- /test-helpers.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | function test_line() { 4 | reportType=$1 5 | result_filename=$2 6 | pattern=$3 7 | if grep --quiet "$3" "$result_filename" 8 | then 9 | echo "**PASS** $reportType report has: $pattern" 10 | else 11 | echo "**FAILED** $reportType report is missing: $pattern" && exit 1 12 | fi 13 | } 14 | 15 | function test_report() { 16 | report_type=$1 17 | shift 18 | result_filename=$1 19 | shift 20 | patterns=("$@") 21 | for line in "${patterns[@]}"; 22 | do 23 | test_line $report_type $result_filename "${line[@]}" 24 | done 25 | } 26 | 27 | declare -a lines_to_find=("detected java code. we recommend using OpenJDK" 28 | "detected python code. min version 3.7.5 is required" 29 | "detected python code. if you need pip, version 19.3 or above is recommended" 30 | "dependency library numpy is present. min version 1.19.0 is required" 31 | "detected java code. min version 8 is required. version 17 or above is recommended" 32 | "using dependency library snappy-java version 1.1.3. upgrade to at least version 1.1.4" 33 | "using dependency library hadoop-lzo. this library requires a manual build" 34 | "dependency library: leveldbjni-all is not supported on Ampere Processors" 35 | "detected go code. min version 1.16 is required. version 1.18 or above is recommended" 36 | "using dependency library github.com/golang/snappy version 0.0.1. upgrade to at least version 0.0.2" 37 | ) 38 | -------------------------------------------------------------------------------- /test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Runs unit tests, generates the binary and then runs it against sample projects 4 | 5 | # run unit tests 6 | echo "🐍 Setup virtual environment" 7 | ./setup-environment.sh 8 | if [ $? -ne 0 ]; then 9 | echo "**ERROR**: failed to initialize Python Virtual Environment" && exit 1 10 | fi 11 | 12 | # run unit tests 13 | echo "🔬 Running unit tests" 14 | ./unit-test.sh 15 | if [ $? -ne 0 ]; then 16 | echo "**ERROR**: unit tests failed" && exit 1 17 | fi 18 | 19 | # build project 20 | echo "⚒️ Building project" 21 | ./build.sh 22 | if [ $? -ne 0 ]; then 23 | echo "**ERROR**: failed to build project" && exit 1 24 | fi 25 | 26 | echo "🧪 Running integration tests" 27 | ./integration-test.sh 28 | if [ $? -ne 0 ]; then 29 | echo "**ERROR**: integration tests failed" && exit 1 30 | fi 31 | 32 | # run load tests 33 | echo "⏳ Running load tests" 34 | ./load_test.sh 35 | if [ $? -ne 0 ]; then 36 | echo "**ERROR**: load tests failed" && exit 1 37 | fi 38 | 39 | if hash docker 40 | then 41 | echo "🐋 Running container tests" 42 | ./container-test.sh 43 | if [ $? -ne 0 ]; then 44 | echo "**ERROR**: error generating jar for Ampere Ready Java tool" && exit 1 45 | fi 46 | fi 47 | -------------------------------------------------------------------------------- /tests-baseline/directory_not_found_test.txt: -------------------------------------------------------------------------------- 1 | unexisting_directory: directory not found. 2 | -------------------------------------------------------------------------------- /tests-baseline/missing_arguments_test.txt: -------------------------------------------------------------------------------- 1 | usage: porting-advisor [-h] [--issue-types ISSUE_TYPES] [--no-filter] 2 | [--no-progress] [--output OUTPUT] 3 | [--output-format OUTPUT_FORMAT] [--quiet] 4 | [--target-os TARGET_OS] [--version] 5 | [--logging-level {error,warning,info,debug}] 6 | [--log-file LOG_FILE] [--log-to-console] 7 | DIRECTORY 8 | porting-advisor: error: the following arguments are required: DIRECTORY 9 | -------------------------------------------------------------------------------- /tests-baseline/sample_applications_test.json: -------------------------------------------------------------------------------- 1 | { 2 | "python": { 3 | "type": "git", 4 | "app": "pytorch", 5 | "url": "https://github.com/pytorch/pytorch.git", 6 | "branch": "v2.0.1", 7 | "integrity": { 8 | "architecture-specific AVX-256 intrinsic": 340, 9 | "architecture-specific inline assembly": 45, 10 | "architecture-specific intrinsic": 256 11 | }, 12 | "prompts": [ 13 | "files scanned", 14 | "150 source files are already ported to aarch64", 15 | "46 inline assembly statements or intrinsics already have aarch64 equivalents", 16 | "detected java code. we recommend using OpenJDK version 17 or above release for aarch64.", 17 | "detected python code. if you need pip, version 19.3 or above is recommended.", 18 | "detected python code. min version 3.7.5 is required.", 19 | "detected java code. min version 8 is required. version 17 or above is recommended", 20 | "dependency library numpy", 21 | "dependency library pyyaml", 22 | "dependency library lxml" 23 | ] 24 | }, 25 | "c_cpp": { 26 | "type": "git", 27 | "app": "mysql-server", 28 | "url": "https://github.com/mysql/mysql-server.git", 29 | "branch": "mysql-8.0.33", 30 | "integrity": { 31 | "preprocessor error on aarch64": 2, 32 | "architecture-specific inline assembly": 35, 33 | "architecture-specific intrinsic": 33 34 | }, 35 | "prompts": [ 36 | "files scanned", 37 | "8 inline assembly statements or intrinsics already have aarch64 equivalents", 38 | "detected java code. we recommend using OpenJDK version 17 or above release for aarch64.", 39 | "detected python code. if you need pip, version 19.3 or above is recommended.", 40 | "detected python code. min version 3.7.5 is required.", 41 | "detected java code. min version 8 is required. version 17 or above is recommended.", 42 | "autoconf config.guess recognizes aarch64 architecture" 43 | ] 44 | }, 45 | "java": { 46 | "type": "wget", 47 | "app": "spark-2.3.0-bin-hadoop2.7", 48 | "url": "https://archive.apache.org/dist/spark/spark-2.3.0/spark-2.3.0-bin-hadoop2.7.tgz", 49 | "integrity": { 50 | "JAR has native methods but no libraries found for aarch64/Linux": 10 51 | }, 52 | "prompts": [ 53 | "files scanned", 54 | "detected java code. we recommend using OpenJDK version 17 or above release for aarch64.", 55 | "detected python code. if you need pip, version 19.3 or above is recommended.", 56 | "detected python code. min version 3.7.5 is required.", 57 | "detected java code. min version 8 is required. version 17 or above is recommended." 58 | ] 59 | }, 60 | "go": { 61 | "type": "git", 62 | "app": "cilium", 63 | "url": "https://github.com/cilium/cilium.git", 64 | "branch": "v1.13.2", 65 | "integrity": { 66 | "architecture-specific intrinsic": 1, 67 | "architecture-specific inline assembly": 10, 68 | "architecture-specific assembly source file": 4 69 | }, 70 | "prompts": [ 71 | "files scanned", 72 | "detected python code. if you need pip, version 19.3 or above is recommended.", 73 | "detected python code. min version 3.7.5 is required.", 74 | "detected go code. min version 1.16 is required. version 1.18 or above is recommended." 75 | ] 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /unit-test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "🐍 Making sure Python Virtual Environment is active" 4 | . .venv/bin/activate 5 | if [ $? -ne 0 ]; then 6 | echo "**ERROR**: could not activate Python Virtual Environment." && exit 1 7 | fi 8 | 9 | # run unit tests 10 | echo "🔬 *** running unit tests ***" 11 | python -m coverage run --source=./src -m unittest discover -s unittest -p "test_*.py" -v 12 | if [ $? -ne 0 ]; then 13 | echo "**ERROR**: unit tests failed" && exit 1 14 | fi -------------------------------------------------------------------------------- /unittest/test_asm_source_scanner.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright 2017 Arm Ltd. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | SPDX-License-Identifier: Apache-2.0 17 | """ 18 | 19 | import io 20 | import unittest 21 | from src.advisor.reports.report import Report 22 | from src.advisor.scanners.asm_source_scanner import AsmSourceScanner 23 | 24 | 25 | class TestAsmSourceScanner(unittest.TestCase): 26 | def test_accepts_file(self): 27 | asm_source_scanner = AsmSourceScanner() 28 | self.assertFalse(asm_source_scanner.accepts_file('test.c')) 29 | self.assertFalse(asm_source_scanner.accepts_file('tests')) 30 | self.assertTrue(asm_source_scanner.accepts_file('test.s')) 31 | self.assertTrue(asm_source_scanner.accepts_file('test.S')) 32 | 33 | def test_scan_file_object(self): 34 | asm_source_scanner = AsmSourceScanner() 35 | report = Report('/root') 36 | io_object = io.StringIO('__asm__("")') 37 | asm_source_scanner.scan_file_object('test.s', io_object, report) 38 | self.assertEqual(len(report.issues), 0) 39 | report = Report('/root') 40 | io_object = io.StringIO('__asm__("mov r0, r1")') 41 | asm_source_scanner.scan_file_object('test.s', io_object, report) 42 | self.assertEqual(len(report.issues), 1) 43 | -------------------------------------------------------------------------------- /unittest/test_config_guess_scanner.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright 2017 Arm Ltd. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | SPDX-License-Identifier: Apache-2.0 17 | """ 18 | 19 | import io 20 | import unittest 21 | from src.advisor.reports.report import Report 22 | from src.advisor.scanners.config_guess_scanner import ConfigGuessScanner 23 | 24 | 25 | class TestConfigGuessScanner(unittest.TestCase): 26 | def test_accepts_file(self): 27 | config_guess_scanner = ConfigGuessScanner() 28 | self.assertFalse(config_guess_scanner.accepts_file('test')) 29 | self.assertTrue(config_guess_scanner.accepts_file('config.guess')) 30 | 31 | def test_scan_file_object(self): 32 | config_guess_scanner = ConfigGuessScanner() 33 | report = Report('/root') 34 | io_object = io.StringIO('xxx') 35 | config_guess_scanner.scan_file_object( 36 | 'config.guess', io_object, report) 37 | self.assertEqual(len(report.issues), 1) 38 | report = Report('/root') 39 | io_object = io.StringIO('aarch64:Linux') 40 | config_guess_scanner.scan_file_object( 41 | 'config.guess', io_object, report) 42 | self.assertEqual(len(report.remarks), 1) 43 | -------------------------------------------------------------------------------- /unittest/test_continuation_parser.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright 2018 Arm Ltd. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | SPDX-License-Identifier: Apache-2.0 17 | """ 18 | 19 | import unittest 20 | from src.advisor.parsers.continuation_parser import ContinuationParser 21 | 22 | 23 | class TestOsFilter(unittest.TestCase): 24 | def test_parse_line(self): 25 | continuation_parser = ContinuationParser() 26 | line = continuation_parser.parse_line('just a line') 27 | self.assertEqual(line, 'just a line') 28 | line = continuation_parser.parse_line('') 29 | self.assertEqual(line, '') 30 | line = continuation_parser.parse_line('#define MACRO \\') 31 | self.assertIsNone(line) 32 | line = continuation_parser.parse_line('first line of macro \\') 33 | self.assertIsNone(line) 34 | line = continuation_parser.parse_line('second line of macro') 35 | self.assertEqual(line, '#define MACRO first line of macro second line of macro') 36 | -------------------------------------------------------------------------------- /unittest/test_csv_report.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright 2017 Arm Ltd. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | SPDX-License-Identifier: Apache-2.0 17 | """ 18 | 19 | import csv 20 | import io 21 | import tempfile 22 | import unittest 23 | from src.advisor.reports.csv_report import CsvReport 24 | from src.advisor.scanners.config_guess_scanner import ConfigGuessScanner 25 | from src.advisor.scanners.source_scanner import SourceScanner 26 | 27 | 28 | class TestCsvReport(unittest.TestCase): 29 | def test_output(self): 30 | config_guess_scanner = ConfigGuessScanner() 31 | source_scanner = SourceScanner() 32 | 33 | report = CsvReport('/root') 34 | report.add_source_file('test_negative.c') 35 | io_object = io.StringIO('__asm__("mov r0, r1")') 36 | source_scanner.scan_file_object( 37 | 'test_negative.c', io_object, report) 38 | report.add_source_file('test_neutral.c') 39 | io_object = io.StringIO('#pragma simd foo') 40 | source_scanner.scan_file_object( 41 | 'test_neutral.c', io_object, report) 42 | report.add_source_file('config.guess') 43 | io_object = io.StringIO('aarch64:Linux') 44 | config_guess_scanner.scan_file_object( 45 | 'config.guess', io_object, report) 46 | self.assertEqual(len(report.issues), 2) 47 | self.assertEqual(len(report.remarks), 1) 48 | 49 | with tempfile.NamedTemporaryFile(mode='w', delete=False) as ofp: 50 | report.write(ofp) 51 | fname = ofp.name 52 | ofp.close() 53 | 54 | with open(fname) as ifp: 55 | csv_reader = csv.DictReader(ifp) 56 | seen_issue1 = False 57 | seen_issue2 = False 58 | for row in csv_reader: 59 | if 'test_negative.c' in row['filename']: 60 | self.assertIn('InlineAsm', row['issue_type']) 61 | seen_issue1 = True 62 | elif 'test_neutral.c' in row['filename']: 63 | self.assertIn('PragmaSimd', row['issue_type']) 64 | seen_issue2 = True 65 | else: 66 | self.fail('Unexpected row in CSV output') 67 | self.assertTrue(seen_issue1) 68 | self.assertTrue(seen_issue2) 69 | -------------------------------------------------------------------------------- /unittest/test_find_port.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright 2017 Arm Ltd. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | SPDX-License-Identifier: Apache-2.0 17 | """ 18 | 19 | import os 20 | import unittest 21 | from src.advisor.helpers.find_port import * 22 | 23 | 24 | class TestFindPort(unittest.TestCase): 25 | def test_port_filenames(self): 26 | self.assertListEqual(port_filenames("filename"), []) 27 | self.assertListEqual(port_filenames("otherarch"), 28 | ['arm', 'aarch64', 'arm64', 'neon', 'sve']) 29 | self.assertListEqual(port_filenames("source-otherarch.c"), 30 | ['source-arm.c', 'source-aarch64.c', 31 | 'source-arm64.c', 'source-neon.c', 32 | 'source-sve.c']) 33 | 34 | def test_find_port_dir(self): 35 | self.assertEqual(find_port_dir('/foo/otherarch', 36 | ['/foo/otherarch', '/foo/aarch64']), 37 | '/foo/aarch64') 38 | 39 | @unittest.skipIf(os.name == 'nt', 'Test fails in Windows') 40 | def test_find_port_file(self): 41 | self.assertEqual(find_port_file('/foo/source-otherarch.c', 42 | ['/foo/source-otherarch.c', 43 | '/foo/source-aarch64.c']), 44 | '/foo/source-aarch64.c') 45 | self.assertEqual(find_port_file('/foo/otherarch/source.c', 46 | ['/foo/otherarch/source.c', 47 | '/foo/aarch64/source.c']), 48 | '/foo/aarch64/source.c') 49 | 50 | @unittest.skipUnless(os.name == 'nt', 'test_find_port_file for Windows only') 51 | def test_find_port_file_windows(self): 52 | self.assertEqual(find_port_file('\\foo\\source-otherarch.c', 53 | ['\\foo\\source-otherarch.c', 54 | '\\foo\\source-aarch64.c']), 55 | '\\foo\\source-aarch64.c') 56 | self.assertEqual(find_port_file('\\foo\\otherarch\\source.c', 57 | ['\\foo\\otherarch\\source.c', 58 | '\\foo\\aarch64\\source.c']), 59 | '\\foo\\aarch64\\source.c') -------------------------------------------------------------------------------- /unittest/test_issue_type_filter.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright 2018,2020 Arm Ltd. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | SPDX-License-Identifier: Apache-2.0 17 | """ 18 | 19 | import unittest 20 | from src.advisor.filters.issue_type_filter import IssueTypeFilter 21 | from src.advisor.reports.issues.inline_asm_issue import InlineAsmIssue 22 | from src.advisor.reports.issues.issue_type_config import IssueTypeConfig 23 | from src.advisor.reports.report import Report 24 | 25 | 26 | class TestIssueTypeFilter(unittest.TestCase): 27 | def test_finalize(self): 28 | report = Report('/root') 29 | issue_type_config = IssueTypeConfig(None) 30 | issue_type_filter = IssueTypeFilter(issue_type_config) 31 | issue_type_filter.initialize_report(report) 32 | report.add_source_file('test.c') 33 | report.add_issue(InlineAsmIssue('test.c', 123)) 34 | issue_type_filter.finalize_report(report) 35 | self.assertEqual(len(report.issues), 1) 36 | 37 | report = Report('/root') 38 | issue_type_config = IssueTypeConfig('-InlineAsm') 39 | issue_type_filter = IssueTypeFilter(issue_type_config) 40 | issue_type_filter.initialize_report(report) 41 | report.add_source_file('test.c') 42 | report.add_issue(InlineAsmIssue('test.c', 123)) 43 | issue_type_filter.finalize_report(report) 44 | self.assertEqual(len(report.issues), 0) 45 | 46 | report = Report('/root') 47 | issue_type_config = IssueTypeConfig('InlineAsm') 48 | issue_type_filter = IssueTypeFilter(issue_type_config) 49 | issue_type_filter.initialize_report(report) 50 | report.add_source_file('test.c') 51 | report.add_issue(InlineAsmIssue('test.c', 123)) 52 | issue_type_filter.finalize_report(report) 53 | self.assertEqual(len(report.issues), 1) 54 | 55 | report = Report('/root') 56 | issue_type_config = IssueTypeConfig('PreprocessorError') 57 | issue_type_filter = IssueTypeFilter(issue_type_config) 58 | issue_type_filter.initialize_report(report) 59 | report.add_source_file('test.c') 60 | report.add_issue(InlineAsmIssue('test.c', 123)) 61 | issue_type_filter.finalize_report(report) 62 | self.assertEqual(len(report.issues), 0) 63 | -------------------------------------------------------------------------------- /unittest/test_item_type.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright 2020 Arm Ltd. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | SPDX-License-Identifier: Apache-2.0 17 | """ 18 | 19 | import unittest 20 | from src.advisor.reports.issues.compiler_specific_issue import CompilerSpecificIssue 21 | from src.advisor.reports.report_item import ReportItem 22 | 23 | 24 | class TestItemType(unittest.TestCase): 25 | def test_compiler_specific_issue(self): 26 | issue = CompilerSpecificIssue('filename', 123, 'compiler', 'function') 27 | self.assertEqual(issue.item_type, ReportItem.NEUTRAL) 28 | -------------------------------------------------------------------------------- /unittest/test_java_tool_invoker.py: -------------------------------------------------------------------------------- 1 | import os 2 | import unittest 3 | import urllib.request 4 | from src.advisor.helpers.java.java_tool_invoker import JavaToolInvoker 5 | 6 | 7 | class TestJavaToolIncoker(unittest.TestCase): 8 | def setUp(self) -> None: 9 | self.tool_invoker = JavaToolInvoker() 10 | 11 | def test_can_run_checks_java_is_installed(self): 12 | self.assertTrue(self.tool_invoker.can_run()) 13 | 14 | def test_ampere_ready_assessor_for_jars_with_native_methods(self): 15 | download_url = 'https://repo.maven.apache.org/maven2/io/netty/netty-transport-native-unix-common/4.1.73.Final/netty-transport-native-unix-common-4.1.73.Final.jar' 16 | path = os.path.join('sample-projects', 'java-samples', 'netty-transport-native-unix-common-4.1.73.Final.jar') 17 | urllib.request.urlretrieve(download_url, path) 18 | result, message = self.tool_invoker.ampere_ready_assessor(path) 19 | os.remove(path) 20 | self.assertEqual(3, result) 21 | self.assertTrue(message.startswith('Native methods:')) 22 | 23 | def test_ampere_ready_assessor_for_jars_with_non_native_methods(self): 24 | download_url = 'https://repo.maven.apache.org/maven2/javax/activation/activation/1.1.1/activation-1.1.1.jar' 25 | path = os.path.join('sample-projects', 'java-samples', 'activation-1.1.1.jar') 26 | urllib.request.urlretrieve(download_url, path) 27 | result, message = self.tool_invoker.ampere_ready_assessor(path) 28 | os.remove(path) 29 | self.assertEqual(0, result) 30 | self.assertEqual('No native methods found in scanned JAR files.', message) 31 | -------------------------------------------------------------------------------- /unittest/test_meson_scanner.py: -------------------------------------------------------------------------------- 1 | """ 2 | SPDX-License-Identifier: Apache-2.0 3 | Copyright (c) 2024, Ampere Computing LLC 4 | """ 5 | 6 | import io 7 | import unittest 8 | from src.advisor.reports.report import Report 9 | from src.advisor.scanners.meson_scanner import MesonScanner 10 | 11 | 12 | class TestMesonScanner(unittest.TestCase): 13 | def test_accepts_file(self): 14 | meson_scanner = MesonScanner() 15 | self.assertFalse(meson_scanner.accepts_file('test')) 16 | self.assertTrue(meson_scanner.accepts_file('meson.build')) 17 | 18 | def test_scan_file_object(self): 19 | meson_scanner = MesonScanner() 20 | report = Report('/root') 21 | io_object = io.StringIO('xxx') 22 | meson_scanner.scan_file_object( 23 | 'meson.build', io_object, report) 24 | self.assertEqual(len(report.issues), 0) 25 | 26 | 27 | def test_arch_specific_libs_re(self): 28 | match = MesonScanner.ARCH_SPECIFIC_LIBS_RE_PROG.search("cc.find_library('foo')") 29 | self.assertIsNone(match) 30 | match = MesonScanner.ARCH_SPECIFIC_LIBS_RE_PROG.search("cc.find_library('otherarch')") 31 | self.assertIsNotNone(match) 32 | self.assertEqual(match.group(1), "otherarch") 33 | 34 | def test_arch_specific_libs(self): 35 | meson_scanner = MesonScanner() 36 | report = Report('/root') 37 | io_object = io.StringIO("cc.find_library('otherarch')") 38 | meson_scanner.scan_file_object( 39 | 'meson.build', io_object, report) 40 | self.assertEqual(len(report.issues), 1) 41 | 42 | def test_neoverse_specific_opts_line_re(self): 43 | match = MesonScanner.NEOVERSE_SPECIFIC_OPTS_RE_PROG.search("'compiler_options': ['-mcpu=ampere1a'],") 44 | self.assertIsNone(match) 45 | match = MesonScanner.NEOVERSE_SPECIFIC_OPTS_RE_PROG.search("'compiler_options': ['-mcpu=neoverse-n2'],") 46 | self.assertIsNotNone(match) 47 | 48 | def test_ampereone_specific_opts_line_re(self): 49 | match = MesonScanner.AMPEREONE_SPECIFIC_OPTS_RE_PROG.search("'compiler_options': ['-mcpu=neoverse-n2'],") 50 | self.assertIsNone(match) 51 | match = MesonScanner.AMPEREONE_SPECIFIC_OPTS_RE_PROG.search("'compiler_options': ['-mcpu=ampere1a'],") 52 | self.assertIsNotNone(match) 53 | 54 | def test_neoverse_specific_opts_line(self): 55 | meson_scanner = MesonScanner() 56 | 57 | report = Report('/root') 58 | io_object = io.StringIO("'compiler_options': ['-mcpu=neoverse-n2'],") 59 | meson_scanner.scan_file_object( 60 | 'meson.build', io_object, report) 61 | # Should report 2 issues, one for neoverse flag indication, another for ampereone flag missing. 62 | self.assertEqual(len(report.issues), 2) 63 | 64 | report = Report('/root') 65 | io_object = io.StringIO("'compiler_options': ['-mcpu=ampere1a'],\n'compiler_options': ['-mtune=ampere1a'],\n'compiler_options': ['-mcpu=neoverse-v2'],\n'compiler_options': ['-mtune=neoverse-v2'],\n") 66 | meson_scanner.scan_file_object( 67 | 'meson.build', io_object, report) 68 | self.assertEqual(len(report.issues), 2) 69 | 70 | 71 | -------------------------------------------------------------------------------- /unittest/test_naive_comment_parser.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright 2018 Arm Ltd. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | SPDX-License-Identifier: Apache-2.0 17 | """ 18 | 19 | import unittest 20 | from src.advisor.parsers.naive_comment_parser import NaiveCommentParser 21 | 22 | 23 | class TestNaiveCommentParser(unittest.TestCase): 24 | def test_parse_line(self): 25 | comment_parser = NaiveCommentParser() 26 | self.assertFalse(comment_parser.parse_line('is not a comment')) 27 | self.assertTrue(comment_parser.parse_line('// single line comment')) 28 | self.assertFalse(comment_parser.parse_line('is not a comment')) 29 | self.assertTrue(comment_parser.parse_line('/* start of multi line comment')) 30 | self.assertTrue(comment_parser.parse_line(' middle of multi line comment')) 31 | self.assertTrue(comment_parser.parse_line('end of multi line comment */')) 32 | self.assertFalse(comment_parser.parse_line('is not a comment')) 33 | self.assertTrue(comment_parser.parse_line('/* single line comment */')) 34 | self.assertFalse(comment_parser.parse_line('is not a comment')) 35 | self.assertFalse(comment_parser.parse_line('comment in /* middle of */ line')) 36 | -------------------------------------------------------------------------------- /unittest/test_port_filter.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright 2017 Arm Ltd. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | SPDX-License-Identifier: Apache-2.0 17 | """ 18 | 19 | import unittest 20 | from src.advisor.filters.port_filter import PortFilter 21 | from src.advisor.reports.issues.inline_asm_issue import InlineAsmIssue 22 | from src.advisor.reports.report import Report 23 | 24 | 25 | class TestPortFilter(unittest.TestCase): 26 | def test_finalize(self): 27 | report = Report('/root') 28 | port_filter = PortFilter() 29 | port_filter.initialize_report(report) 30 | report.add_source_file('test.c') 31 | report.add_issue(InlineAsmIssue('test.c', 123)) 32 | report.add_source_file('test-otherarch.c') 33 | report.add_issue(InlineAsmIssue('test-otherarch.c', 123)) 34 | report.add_source_file('test-aarch64.c') 35 | report.add_issue(InlineAsmIssue('test-aarch64.c', 123)) 36 | port_filter.finalize_report(report) 37 | self.assertEqual(len(report.issues), 1) -------------------------------------------------------------------------------- /unittest/test_python_comment_parser.py: -------------------------------------------------------------------------------- 1 | from src.advisor.parsers.python_comment_parser import PythonCommentParser 2 | import unittest 3 | 4 | 5 | class TestPythonCommentParser(unittest.TestCase): 6 | def test_parse_line(self): 7 | comment_parser = PythonCommentParser() 8 | self.assertFalse(comment_parser.parse_line('is not a comment')) 9 | self.assertTrue(comment_parser.parse_line('# single line comment')) 10 | self.assertFalse(comment_parser.parse_line('is not a comment')) 11 | self.assertTrue(comment_parser.parse_line('""" start of multi line comment')) 12 | self.assertTrue(comment_parser.parse_line(' middle of multi line comment')) 13 | self.assertTrue(comment_parser.parse_line('end of multi line comment """')) 14 | self.assertFalse(comment_parser.parse_line('is not a comment')) 15 | self.assertTrue(comment_parser.parse_line('""" single line comment """')) 16 | self.assertFalse(comment_parser.parse_line('is not a comment')) 17 | self.assertFalse(comment_parser.parse_line('comment in """ middle of """ line')) 18 | -------------------------------------------------------------------------------- /unittest/test_python_requirements_parser.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from src.advisor.parsers.python_requirements_parser import PythonRequirementsParser 3 | 4 | 5 | class TestPythonRequirementsParser(unittest.TestCase): 6 | def setUp(self) -> None: 7 | self.parser = PythonRequirementsParser() 8 | 9 | def test_parse_line_returns_name_version_and_comparer(self): 10 | expected = 'SciPy', '1.7.2', '>=' 11 | self.assertEqual(expected, self.parser.parse_line('SciPy>=1.7.2')) 12 | self.assertEqual(expected, self.parser.parse_line('SciPy >= 1.7.2')) 13 | 14 | def test_parse_line_returns_name(self): 15 | expected = 'SciPy', '', '' 16 | self.assertEqual(expected, self.parser.parse_line('SciPy')) 17 | 18 | def test_parse_line_ignores_comments(self): 19 | expected = 'SciPy', '1.7.2', '>=' 20 | self.assertEqual(expected, self.parser.parse_line('SciPy>=1.7.2 # this is a comment')) 21 | self.assertEqual(expected, self.parser.parse_line('SciPy >= 1.7.2 # this is a comment')) -------------------------------------------------------------------------------- /unittest/test_python_version_checker.py: -------------------------------------------------------------------------------- 1 | import pkg_resources 2 | import platform 3 | import unittest 4 | from src.advisor.helpers.python.python_version_checker import PythonVersionChecker 5 | 6 | class TestPythonVersionChecker(unittest.TestCase): 7 | def test_get_package_version_returns_non_empty_string_for_existing_library(self): 8 | pip_version = pkg_resources.get_distribution('Jinja2').version 9 | self.assertEqual(pip_version, PythonVersionChecker.get_package_version('Jinja2')) 10 | 11 | def test_get_package_version_returns_none_for_fake_library(self): 12 | version = PythonVersionChecker.get_package_version('myFakeLibraryThatDoesNotExist') 13 | self.assertIsNone(version) 14 | 15 | def test_get_python_version_returns_current_version(self): 16 | runtime_version = platform.python_version() 17 | self.assertEqual(runtime_version, PythonVersionChecker.get_python_version()) 18 | 19 | def test_get_pip_version_returns_current_version(self): 20 | pip_version = pkg_resources.get_distribution('pip').version 21 | self.assertEqual(pip_version, PythonVersionChecker.get_pip_version()) -------------------------------------------------------------------------------- /unittest/test_report_factory.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright 2020 Arm Ltd. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | SPDX-License-Identifier: Apache-2.0 17 | """ 18 | 19 | import unittest 20 | from src.advisor.reports.report_factory import ReportFactory, ReportOutputFormat 21 | 22 | 23 | class TestReportFactory(unittest.TestCase): 24 | def test_output_format_from_extension(self): 25 | report_factory = ReportFactory() 26 | self.assertEqual(report_factory.output_format_for_extension('txt'), ReportOutputFormat.TEXT) 27 | self.assertEqual(report_factory.output_format_for_extension('html'), ReportOutputFormat.HTML) 28 | self.assertEqual(report_factory.output_format_for_extension('htm'), ReportOutputFormat.HTML) 29 | self.assertEqual(report_factory.output_format_for_extension('json'), ReportOutputFormat.JSON) 30 | self.assertEqual(report_factory.output_format_for_extension('csv'), ReportOutputFormat.CSV) 31 | -------------------------------------------------------------------------------- /unittest/test_ruby_gem_parser.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from src.advisor.parsers.ruby_gem_parser import RubyGemParser 3 | 4 | 5 | class TestRubyGemParser(unittest.TestCase): 6 | def setUp(self) -> None: 7 | self.parser = RubyGemParser() 8 | 9 | def test_gem_with_name_returns_name(self): 10 | expected = 'rails', None 11 | self.assertEqual(expected, self.parser.parse_line('"rails"')) 12 | self.assertEqual(expected, self.parser.parse_line("'rails'")) 13 | 14 | def test_gem_with_name_and_version_returns_name_and_version(self): 15 | expected = 'rails', '6.1.6.1' 16 | self.assertEqual(expected, self.parser.parse_line('"rails", "6.1.6.1"')) 17 | self.assertEqual(expected, self.parser.parse_line("'rails', '6.1.6.1'")) 18 | 19 | def test_gem_with_name_version_and_specifier_returns_name(self): 20 | expected = 'rails', '6.1.6.1' 21 | self.assertEqual(expected, self.parser.parse_line("'rails', '~> 6.1.6.1'")) 22 | self.assertEqual(expected, self.parser.parse_line('"rails", "~> 6.1.6.1"')) 23 | self.assertEqual(expected, self.parser.parse_line('"rails", "<= 6.1.6.1"')) 24 | self.assertEqual(expected, self.parser.parse_line('"rails", ">= 6.1.6.1"')) 25 | self.assertEqual(expected, self.parser.parse_line('"rails", ">= 6.1.6.1", "< 7.0.0"')) 26 | 27 | def test_gem_with_ternary_operator_returns_correct_value(self): 28 | expected = 'cucumber', '4.1' 29 | self.assertEqual(expected, self.parser.parse_line('"cucumber", RUBY_VERSION >= "2.5" ? "~> 5.1.2" : "~> 4.1"')) 30 | 31 | def test_gem_with_other_specifiers_returns_correct_value(self): 32 | expected = 'gssapi', None 33 | self.assertEqual(expected, self.parser.parse_line('"gssapi", group: :kerberos')) 34 | self.assertEqual(expected, self.parser.parse_line('"gssapi", require: false, platform: :mri')) 35 | self.assertEqual(expected, self.parser.parse_line('"gssapi", git: "https://github.com/gssapi"')) -------------------------------------------------------------------------------- /unittest/test_rules_loader.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from src.advisor.helpers.rules_loader import RulesLoader 3 | 4 | class TestRulesLoader(unittest.TestCase): 5 | def test_get_rules_gets_valid_object_for_existing_rules_file(self): 6 | rules = RulesLoader.get_rules('python') 7 | name = rules['languageRules']['name'] 8 | self.assertIsNotNone(rules) 9 | self.assertEqual('Python', name) 10 | 11 | def test_get_rules_gets_none_for_unexisting_file(self): 12 | rules = RulesLoader.get_rules('fake_language') 13 | self.assertIsNone(rules) --------------------------------------------------------------------------------