mirror of
https://github.com/facebook/rocksdb.git
synced 2026-07-07 22:55:23 +08:00
Compare commits
27 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e69ce1041f | |||
| e3169e3ea8 | |||
| 62976c633e | |||
| d46141e11d | |||
| 0eeeda89ff | |||
| 3c0bb7fdd4 | |||
| e1eb14133a | |||
| 650acf64d6 | |||
| 172b9ec84c | |||
| 2407812292 | |||
| d70f081aa7 | |||
| b96d31b60f | |||
| 9840ed9eef | |||
| 7ff41fea6e | |||
| a372c5b00f | |||
| 3a10ffe954 | |||
| a28b2cf9ed | |||
| 1d65661837 | |||
| fdc9b5248b | |||
| d039c41b43 | |||
| 08d5a83499 | |||
| ab3e6f6476 | |||
| 5042d04a96 | |||
| 7b1a12f182 | |||
| ada6e87f5a | |||
| 4f3ed8622e | |||
| 222f84e2c3 |
-86
@@ -1,86 +0,0 @@
|
||||
# When making changes, verify the output of:
|
||||
# clang-tidy -list-checks
|
||||
---
|
||||
Checks: "-*,\
|
||||
bugprone-argument-comment,\
|
||||
bugprone-dangling-handle,\
|
||||
bugprone-fold-init-type,\
|
||||
bugprone-forward-declaration-namespace,\
|
||||
bugprone-forwarding-reference-overload,\
|
||||
bugprone-shadow,\
|
||||
bugprone-sizeof-*,\
|
||||
bugprone-string-constructor,\
|
||||
bugprone-undefined-memory-manipulation,\
|
||||
bugprone-unused-return-value,\
|
||||
bugprone-use-after-move,\
|
||||
cert-env33-c,\
|
||||
cert-err58-cpp,\
|
||||
cert-msc30-c,\
|
||||
cert-msc50-cpp,\
|
||||
clang-analyzer-*,\
|
||||
clang-diagnostic-*,\
|
||||
-clang-diagnostic-missing-designated-field-initializers,\
|
||||
concurrency-mt-unsafe,\
|
||||
cppcoreguidelines-avoid-non-const-global-variables,\
|
||||
cppcoreguidelines-missing-std-forward,\
|
||||
cppcoreguidelines-pro-type-member-init,\
|
||||
cppcoreguidelines-special-member-functions,\
|
||||
cppcoreguidelines-virtual-class-destructor,\
|
||||
google-build-using-namespace,\
|
||||
google-explicit-constructor,\
|
||||
google-readability-avoid-underscore-in-googletest-name,\
|
||||
misc-definitions-in-headers,\
|
||||
misc-redundant-expression,\
|
||||
modernize-make-shared,\
|
||||
modernize-use-emplace,\
|
||||
modernize-use-noexcept,\
|
||||
modernize-use-override,\
|
||||
modernize-use-using,\
|
||||
performance-faster-string-find,\
|
||||
performance-for-range-copy,\
|
||||
performance-implicit-conversion-in-loop,\
|
||||
performance-inefficient-algorithm,\
|
||||
performance-inefficient-string-concatenation,\
|
||||
performance-inefficient-vector-operation,\
|
||||
performance-move-const-arg,\
|
||||
performance-move-constructor-init,\
|
||||
performance-no-automatic-move,\
|
||||
performance-no-int-to-ptr,\
|
||||
performance-noexcept-move-constructor,\
|
||||
performance-noexcept-swap,\
|
||||
performance-trivially-destructible,\
|
||||
performance-type-promotion-in-math-fn,\
|
||||
performance-unnecessary-copy-initialization,\
|
||||
performance-unnecessary-value-param,\
|
||||
readability-braces-around-statements,\
|
||||
readability-duplicate-include,\
|
||||
readability-isolate-declaration,\
|
||||
readability-operators-representation,\
|
||||
readability-redundant-string-init"
|
||||
|
||||
WarningsAsErrors: "bugprone-use-after-move"
|
||||
|
||||
CheckOptions:
|
||||
- key: bugprone-easily-swappable-parameters.MinimumLength
|
||||
value: 4
|
||||
- key: cppcoreguidelines-avoid-non-const-global-variables.AllowThreadLocal
|
||||
value: true
|
||||
- key: cppcoreguidelines-special-member-functions.AllowSoleDefaultDtor
|
||||
value: true
|
||||
- key: cppcoreguidelines-special-member-functions.AllowImplicitlyDeletedCopyOrMove
|
||||
value: true
|
||||
- key: modernize-use-using.IgnoreExternC
|
||||
value: true
|
||||
- key: performance-move-const-arg.CheckTriviallyCopyableMove
|
||||
value: false
|
||||
- key: performance-unnecessary-value-param.AllowedTypes
|
||||
value: '[Pp]ointer$;[Pp]tr$;[Rr]ef(erence)?$'
|
||||
- key: performance-unnecessary-copy-initialization.AllowedTypes
|
||||
value: '[Pp]ointer$;[Pp]tr$;[Rr]ef(erence)?$'
|
||||
- key: readability-operators-representation.BinaryOperators
|
||||
value: '&&;&=;&;|;~;!;!=;||;|=;^;^='
|
||||
- key: readability-redundant-string-init.StringNames
|
||||
value: '::std::basic_string'
|
||||
- key: readability-named-parameter.InsertPlainNamesInForwardDecls
|
||||
value: true
|
||||
...
|
||||
@@ -1,26 +0,0 @@
|
||||
name: build-folly
|
||||
description: Build folly and dependencies (skipped if cache hit)
|
||||
inputs:
|
||||
cache-hit:
|
||||
description: Whether the folly cache was hit
|
||||
required: true
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Build folly and dependencies
|
||||
if: ${{ inputs.cache-hit != 'true' }}
|
||||
run: |
|
||||
clean_path=()
|
||||
IFS=: read -ra path_entries <<< "$PATH"
|
||||
for entry in "${path_entries[@]}"; do
|
||||
if [[ "$entry" != "/usr/lib/ccache" ]]; then
|
||||
clean_path+=("$entry")
|
||||
fi
|
||||
done
|
||||
export PATH="$(IFS=:; echo "${clean_path[*]}")"
|
||||
make build_folly
|
||||
shell: bash
|
||||
- name: Skip folly build (using cached version)
|
||||
if: ${{ inputs.cache-hit == 'true' }}
|
||||
run: echo "Folly build skipped - using cached version"
|
||||
shell: bash
|
||||
@@ -1,8 +0,0 @@
|
||||
name: build-for-benchmarks
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- name: Linux build for benchmarks
|
||||
run: make V=1 J=8 -j8 release
|
||||
shell: bash
|
||||
@@ -1,33 +0,0 @@
|
||||
name: cache-folly
|
||||
description: Cache folly build to speed up CI
|
||||
outputs:
|
||||
cache-hit:
|
||||
description: Whether the cache was hit
|
||||
value: ${{ steps.cache-folly-build.outputs.cache-hit }}
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Extract FOLLY_MK_HASH
|
||||
id: extract-folly-hash
|
||||
shell: bash
|
||||
run: |
|
||||
FOLLY_MK_HASH=$(md5sum folly.mk | cut -d' ' -f1)
|
||||
echo "hash=$FOLLY_MK_HASH" >> $GITHUB_OUTPUT
|
||||
- name: Extract FOLLY_INSTALL_DIR
|
||||
id: extract-folly-install-dir
|
||||
shell: bash
|
||||
run: |
|
||||
FOLLY_INSTALL_DIR=$(cd third-party/folly && python3 build/fbcode_builder/getdeps.py show-inst-dir)
|
||||
echo "dir=$(echo $FOLLY_INSTALL_DIR | sed 's|installed/folly|installed|')" >> $GITHUB_OUTPUT
|
||||
- name: Cache folly build
|
||||
id: cache-folly-build
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
# Cache the folly build directory
|
||||
path: ${{ steps.extract-folly-install-dir.outputs.dir }}
|
||||
# Key is based on:
|
||||
# - OS and architecture
|
||||
# - The docker image, which may not always be specified/known
|
||||
# - Hash of folly.mk, which includes the folly repository commit hash
|
||||
# NOTE: this is still only intended for DEBUG folly builds
|
||||
key: folly-build-${{ runner.os }}-${{ runner.arch }}-${{ github.job_container.image }}-${{ steps.extract-folly-hash.outputs.hash }}-${{ env.PORTABLE == '1' && 'portable' || 'native' }}
|
||||
@@ -1,21 +0,0 @@
|
||||
name: cache-getdeps-downloads
|
||||
description: Cache getdeps downloads to avoid unreliable mirrors and speed up builds
|
||||
outputs:
|
||||
cache-hit:
|
||||
description: Whether the cache was hit
|
||||
value: ${{ steps.cache-downloads.outputs.cache-hit }}
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Cache getdeps downloads
|
||||
id: cache-downloads
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
# Use a fixed path that we control - folly.mk will sync with getdeps downloads dir
|
||||
path: /tmp/rocksdb-getdeps-cache
|
||||
# Use a rolling cache key - the cache accumulates downloads over time
|
||||
# The key includes a weekly timestamp to ensure periodic refresh
|
||||
key: getdeps-downloads-${{ runner.os }}-${{ runner.arch }}-week-${{ github.run_id }}
|
||||
restore-keys: |
|
||||
getdeps-downloads-${{ runner.os }}-${{ runner.arch }}-week-
|
||||
getdeps-downloads-${{ runner.os }}-${{ runner.arch }}-
|
||||
@@ -1,10 +0,0 @@
|
||||
name: increase-max-open-files-on-macos
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Increase max open files
|
||||
run: |-
|
||||
sudo sysctl -w kern.maxfiles=1048576
|
||||
sudo sysctl -w kern.maxfilesperproc=1048576
|
||||
sudo launchctl limit maxfiles 1048576
|
||||
shell: bash
|
||||
@@ -1,7 +0,0 @@
|
||||
name: install-gflags-on-macos
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Install gflags on macos
|
||||
run: HOMEBREW_NO_AUTO_UPDATE=1 brew install gflags
|
||||
shell: bash
|
||||
@@ -1,7 +0,0 @@
|
||||
name: install-gflags
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Install gflags
|
||||
run: sudo apt-get update -y && sudo apt-get install -y libgflags-dev
|
||||
shell: bash
|
||||
@@ -1,11 +0,0 @@
|
||||
name: install-maven
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Install Maven
|
||||
run: |
|
||||
wget --no-check-certificate https://archive.apache.org/dist/maven/maven-3/3.9.11/binaries/apache-maven-3.9.11-bin.tar.gz
|
||||
tar zxf apache-maven-3.9.11-bin.tar.gz
|
||||
echo "export M2_HOME=$(pwd)/apache-maven-3.9.11" >> $GITHUB_ENV
|
||||
echo "$(pwd)/apache-maven-3.9.11/bin" >> $GITHUB_PATH
|
||||
shell: bash
|
||||
@@ -1,22 +0,0 @@
|
||||
name: perform-benchmarks
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Test low-variance benchmarks
|
||||
run: "./tools/benchmark_ci.py --db_dir ${{ runner.temp }}/rocksdb-benchmark-datadir --output_dir ${{ runner.temp }}/benchmark-results --num_keys 20000000"
|
||||
env:
|
||||
LD_LIBRARY_PATH: "/usr/local/lib"
|
||||
DURATION_RO: 300
|
||||
DURATION_RW: 500
|
||||
NUM_THREADS: 1
|
||||
MAX_BACKGROUND_JOBS: 4
|
||||
CI_TESTS_ONLY: 'true'
|
||||
WRITE_BUFFER_SIZE_MB: 16
|
||||
TARGET_FILE_SIZE_BASE_MB: 16
|
||||
MAX_BYTES_FOR_LEVEL_BASE_MB: 64
|
||||
COMPRESSION_TYPE: none
|
||||
CACHE_INDEX_AND_FILTER_BLOCKS: 1
|
||||
MIN_LEVEL_TO_COMPRESS: 3
|
||||
CACHE_SIZE_MB: 10240
|
||||
MB_WRITE_PER_SEC: 2
|
||||
shell: bash
|
||||
@@ -1,17 +0,0 @@
|
||||
name: post-benchmarks
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Upload Benchmark Results artifact
|
||||
uses: actions/upload-artifact@v4.0.0
|
||||
with:
|
||||
name: benchmark-results
|
||||
path: "${{ runner.temp }}/benchmark-results/**"
|
||||
if-no-files-found: error
|
||||
- name: Send benchmark report to visualisation
|
||||
run: |-
|
||||
set +e
|
||||
set +o pipefail
|
||||
./build_tools/benchmark_log_tool.py --tsvfile ${{ runner.temp }}/benchmark-results/report.tsv --esdocument https://search-rocksdb-bench-k2izhptfeap2hjfxteolsgsynm.us-west-2.es.amazonaws.com/bench_test3_rix/_doc
|
||||
true
|
||||
shell: bash
|
||||
@@ -1,38 +0,0 @@
|
||||
name: post-steps
|
||||
description: Steps that are taken after a RocksDB job
|
||||
inputs:
|
||||
artifact-prefix:
|
||||
description: Prefix to append to the name of artifacts that are uploaded
|
||||
required: true
|
||||
default: "${{ github.job }}"
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Upload Test Results artifact
|
||||
uses: actions/upload-artifact@v4.0.0
|
||||
with:
|
||||
name: "${{ inputs.artifact-prefix }}-test-results"
|
||||
path: "${{ runner.temp }}/test-results/**"
|
||||
- name: Upload DB LOG file artifact
|
||||
uses: actions/upload-artifact@v4.0.0
|
||||
with:
|
||||
name: "${{ inputs.artifact-prefix }}-db-log-file"
|
||||
path: LOG
|
||||
- name: Copy Test Logs (on Failure)
|
||||
if: ${{ failure() }}
|
||||
run: |
|
||||
mkdir -p ${{ runner.temp }}/failure-test-logs
|
||||
cp -r t/* ${{ runner.temp }}/failure-test-logs
|
||||
shell: bash
|
||||
- name: Upload Test Logs (on Failure) artifact
|
||||
uses: actions/upload-artifact@v4.0.0
|
||||
with:
|
||||
name: "${{ inputs.artifact-prefix }}-failure-test-logs"
|
||||
path: ${{ runner.temp }}/failure-test-logs/**
|
||||
if-no-files-found: ignore
|
||||
- name: Upload Core Dumps artifact
|
||||
uses: actions/upload-artifact@v4.0.0
|
||||
with:
|
||||
name: "${{ inputs.artifact-prefix }}-core-dumps"
|
||||
path: "core.*"
|
||||
if-no-files-found: ignore
|
||||
@@ -1,5 +0,0 @@
|
||||
name: pre-steps-macos
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
@@ -1,28 +0,0 @@
|
||||
name: pre-steps
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Dump ulimits for diagnostics
|
||||
run: |
|
||||
echo "=== ulimits (soft) ==="
|
||||
ulimit -a -S || true
|
||||
echo "=== ulimits (hard) ==="
|
||||
ulimit -a -H || true
|
||||
shell: bash
|
||||
- name: Install lld linker for faster builds
|
||||
run: apt-get update -y && apt-get install -y lld 2>/dev/null || true
|
||||
shell: bash
|
||||
- name: Setup Environment Variables
|
||||
run: |-
|
||||
echo "GTEST_THROW_ON_FAILURE=0" >> "$GITHUB_ENV"
|
||||
echo "GTEST_OUTPUT=\"xml:${{ runner.temp }}/test-results/\"" >> "$GITHUB_ENV"
|
||||
echo "SKIP_FORMAT_BUCK_CHECKS=1" >> "$GITHUB_ENV"
|
||||
echo "GTEST_COLOR=1" >> "$GITHUB_ENV"
|
||||
echo "CTEST_OUTPUT_ON_FAILURE=1" >> "$GITHUB_ENV"
|
||||
echo "CTEST_TEST_TIMEOUT=300" >> "$GITHUB_ENV"
|
||||
echo "ZLIB_DOWNLOAD_BASE=https://rocksdb-deps.s3.us-west-2.amazonaws.com/pkgs/zlib" >> "$GITHUB_ENV"
|
||||
echo "BZIP2_DOWNLOAD_BASE=https://rocksdb-deps.s3.us-west-2.amazonaws.com/pkgs/bzip2" >> "$GITHUB_ENV"
|
||||
echo "SNAPPY_DOWNLOAD_BASE=https://rocksdb-deps.s3.us-west-2.amazonaws.com/pkgs/snappy" >> "$GITHUB_ENV"
|
||||
echo "LZ4_DOWNLOAD_BASE=https://rocksdb-deps.s3.us-west-2.amazonaws.com/pkgs/lz4" >> "$GITHUB_ENV"
|
||||
echo "ZSTD_DOWNLOAD_BASE=https://rocksdb-deps.s3.us-west-2.amazonaws.com/pkgs/zstd" >> "$GITHUB_ENV"
|
||||
shell: bash
|
||||
@@ -1,54 +0,0 @@
|
||||
name: setup-ccache
|
||||
description: Setup ccache for faster C++ compilation caching
|
||||
inputs:
|
||||
cache-key-prefix:
|
||||
description: Unique prefix for the cache key (e.g., 'build-linux')
|
||||
required: true
|
||||
portable:
|
||||
description: Set PORTABLE=1 to disable -march=native (set to "false" for jobs linking pre-built Folly)
|
||||
required: false
|
||||
default: "true"
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Set ccache environment variables
|
||||
run: |
|
||||
echo "CCACHE_DIR=${{ github.workspace }}/.ccache" >> $GITHUB_ENV
|
||||
echo "CCACHE_BASEDIR=${{ github.workspace }}" >> $GITHUB_ENV
|
||||
echo "CCACHE_NOHASHDIR=true" >> $GITHUB_ENV
|
||||
echo "CCACHE_COMPILERCHECK=content" >> $GITHUB_ENV
|
||||
echo "CCACHE_SLOPPINESS=clang_index_store,file_stat_matches,include_file_ctime,include_file_mtime,ivfsoverlay,pch_defines,modules,system_headers,time_macros" >> $GITHUB_ENV
|
||||
echo "CCACHE_MAXSIZE=4G" >> $GITHUB_ENV
|
||||
if [ "${{ inputs.portable }}" = "true" ]; then
|
||||
echo "PORTABLE=1" >> $GITHUB_ENV
|
||||
fi
|
||||
shell: bash
|
||||
- name: Restore ccache
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ${{ github.workspace }}/.ccache
|
||||
key: ccache-${{ inputs.cache-key-prefix }}-${{ github.head_ref || github.ref }}-${{ github.sha }}
|
||||
restore-keys: |-
|
||||
ccache-${{ inputs.cache-key-prefix }}-${{ github.head_ref || github.ref }}-
|
||||
ccache-${{ inputs.cache-key-prefix }}-refs/heads/main-
|
||||
- name: Install ccache
|
||||
run: |
|
||||
if [ "$RUNNER_OS" = "macOS" ]; then
|
||||
which ccache || brew install ccache
|
||||
else
|
||||
which ccache || (apt-get update && apt-get install -y ccache)
|
||||
fi
|
||||
shell: bash
|
||||
- name: Add ccache to PATH
|
||||
run: |
|
||||
if [ "$RUNNER_OS" = "macOS" ]; then
|
||||
echo "$(brew --prefix ccache)/libexec" >> $GITHUB_PATH
|
||||
else
|
||||
echo "/usr/lib/ccache" >> $GITHUB_PATH
|
||||
fi
|
||||
shell: bash
|
||||
- name: Zero ccache stats and set build marker
|
||||
run: |
|
||||
ccache -z
|
||||
touch "$CCACHE_DIR/.build_marker"
|
||||
shell: bash
|
||||
@@ -1,11 +0,0 @@
|
||||
name: setup-folly
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Checkout folly sources
|
||||
run: |
|
||||
make checkout_folly
|
||||
shell: bash
|
||||
- name: Install patchelf and libaio
|
||||
run: apt-get update -y && apt-get install -y patchelf libaio-dev
|
||||
shell: bash
|
||||
@@ -1,17 +0,0 @@
|
||||
name: setup-jdk-on-macos
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Set up JDK on macos
|
||||
run: |-
|
||||
set -euo pipefail
|
||||
|
||||
JAVA_HOME="$(/usr/libexec/java_home -v 21)"
|
||||
|
||||
echo "JAVA_HOME=${JAVA_HOME}" >> "$GITHUB_ENV"
|
||||
echo "JAVAC_ARGS=--release 8" >> "$GITHUB_ENV"
|
||||
echo "${JAVA_HOME}/bin" >> "$GITHUB_PATH"
|
||||
echo "Using JDK at ${JAVA_HOME}"
|
||||
"${JAVA_HOME}/bin/java" -version
|
||||
"${JAVA_HOME}/bin/javac" -version
|
||||
shell: bash
|
||||
@@ -1,20 +0,0 @@
|
||||
name: build-folly
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Fix repo ownership
|
||||
# Needed in some cases, as safe.directory setting doesn't take effect
|
||||
# under env -i
|
||||
run: chown `whoami` . || true
|
||||
shell: bash
|
||||
- name: Set upstream
|
||||
run: git remote add upstream https://github.com/facebook/rocksdb.git
|
||||
shell: bash
|
||||
- name: Fetch upstream
|
||||
run: git fetch upstream
|
||||
shell: bash
|
||||
- name: Git status
|
||||
# NOTE: some old branch builds under check_format_compatible.sh invoke
|
||||
# git under env -i
|
||||
run: git status && git remote -v && env -i git branch
|
||||
shell: bash
|
||||
@@ -1,15 +0,0 @@
|
||||
name: teardown-ccache
|
||||
description: Trim stale ccache entries and print stats
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Trim and print ccache stats
|
||||
run: |
|
||||
if [ -z "$CCACHE_DIR" ]; then
|
||||
echo "teardown-ccache: CCACHE_DIR not set, skipping (setup-ccache may not have run)"
|
||||
exit 0
|
||||
fi
|
||||
.github/scripts/ccache-trim.sh || true
|
||||
ccache -s || echo "teardown-ccache: ccache not found, skipping stats"
|
||||
if: always()
|
||||
shell: bash
|
||||
@@ -1,140 +0,0 @@
|
||||
name: windows-build-steps
|
||||
inputs:
|
||||
suite-run:
|
||||
description: Comma-separated list of test suites to run (empty to skip C++ tests)
|
||||
required: false
|
||||
default: arena_test,db_basic_test,db_test,db_etc2_test,db_merge_operand_test,bloom_test,c_test,coding_test,crc32c_test,dynamic_bloom_test,env_basic_test,env_test,hash_test,random_test
|
||||
run-java:
|
||||
description: Whether to run Java tests
|
||||
required: false
|
||||
default: "true"
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Detect Visual Studio generator
|
||||
id: detect_vs
|
||||
shell: pwsh
|
||||
run: |
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
$vswhere = Join-Path ([Environment]::GetFolderPath("ProgramFilesX86")) "Microsoft Visual Studio\Installer\vswhere.exe"
|
||||
if (!(Test-Path $vswhere)) {
|
||||
throw "vswhere.exe was not found at $vswhere"
|
||||
}
|
||||
|
||||
$vsInstallationsJson = & $vswhere -latest -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -format json -utf8
|
||||
$vsInstallations = @($vsInstallationsJson | ConvertFrom-Json)
|
||||
if ($vsInstallations.Count -eq 0) {
|
||||
throw "No Visual Studio instance with C++ tools was found."
|
||||
}
|
||||
|
||||
$vsInstallation = $vsInstallations[0]
|
||||
$vsMajor = [int]$vsInstallation.catalog.productLineVersion
|
||||
if ($vsMajor -eq 0) {
|
||||
$vsMajor = [int]($vsInstallation.installationVersion.Split(".")[0])
|
||||
}
|
||||
|
||||
$generatorLine = cmake --help | Where-Object {
|
||||
$_ -match "^\*?\s*Visual Studio $vsMajor\s+\d{4}\s*="
|
||||
} | Select-Object -First 1
|
||||
if (!$generatorLine) {
|
||||
throw "CMake does not support a Visual Studio generator for VS major version $vsMajor."
|
||||
}
|
||||
|
||||
$cmakeGenerator = ($generatorLine -replace "^\*?\s*", "" -replace "\s*=.*$", "").Trim()
|
||||
$vsVersionRange = "[{0}.0,{1}.0)" -f $vsMajor, ($vsMajor + 1)
|
||||
|
||||
echo "Detected Visual Studio at $($vsInstallation.installationPath)"
|
||||
echo "Using CMake generator: $cmakeGenerator"
|
||||
echo "Using setup-msbuild vs-version: $vsVersionRange"
|
||||
|
||||
"CMAKE_GENERATOR=$cmakeGenerator" >> $env:GITHUB_ENV
|
||||
"vs_version=$vsVersionRange" >> $env:GITHUB_OUTPUT
|
||||
- name: Add msbuild to PATH
|
||||
uses: microsoft/setup-msbuild@v2
|
||||
with:
|
||||
vs-version: ${{ steps.detect_vs.outputs.vs_version }}
|
||||
- name: ccache
|
||||
uses: hendrikmuhs/ccache-action@v1.2
|
||||
with:
|
||||
max-size: "10GB"
|
||||
key: ccache-windows-${{ github.workflow }}
|
||||
restore-keys: |
|
||||
ccache-windows-
|
||||
- name: Configure ccache
|
||||
shell: pwsh
|
||||
run: |
|
||||
ccache --set-config=base_dir=C:\a\rocksdb\rocksdb
|
||||
ccache --set-config=hash_dir=false
|
||||
ccache --set-config=compiler_check=content
|
||||
- name: Custom steps
|
||||
env:
|
||||
THIRDPARTY_HOME: ${{ github.workspace }}/thirdparty
|
||||
CMAKE_HOME: C:/Program Files/CMake
|
||||
CMAKE_BIN: C:/Program Files/CMake/bin/cmake.exe
|
||||
CTEST_BIN: C:/Program Files/CMake/bin/ctest.exe
|
||||
SNAPPY_HOME: ${{ github.workspace }}/thirdparty/snappy-1.2.2
|
||||
SNAPPY_INCLUDE: ${{ github.workspace }}/thirdparty/snappy-1.2.2;${{ github.workspace }}/thirdparty/snappy-1.2.2/build
|
||||
SNAPPY_LIB_DEBUG: ${{ github.workspace }}/thirdparty/snappy-1.2.2/build/Debug/snappy.lib
|
||||
run: |-
|
||||
# NOTE: if ... Exit $LASTEXITCODE lines needed to exit and report failure
|
||||
echo ===================== Install Dependencies =====================
|
||||
if (!$env:JAVA_HOME -or !(Test-Path (Join-Path $env:JAVA_HOME "bin\javac.exe"))) {
|
||||
$javac = Get-Command javac -ErrorAction SilentlyContinue
|
||||
if (!$javac) {
|
||||
Write-Error "javac was not found on PATH and JAVA_HOME is not set."
|
||||
Exit 1
|
||||
}
|
||||
$env:JAVA_HOME = Split-Path (Split-Path $javac.Source -Parent) -Parent
|
||||
}
|
||||
echo "JAVA_HOME=$env:JAVA_HOME"
|
||||
& "$env:JAVA_HOME\bin\java.exe" -version
|
||||
& "$env:JAVA_HOME\bin\javac.exe" -version
|
||||
mkdir $Env:THIRDPARTY_HOME
|
||||
cd $Env:THIRDPARTY_HOME
|
||||
echo "Building Snappy dependency..."
|
||||
curl -Lo snappy-1.2.2.zip https://github.com/google/snappy/archive/refs/tags/1.2.2.zip
|
||||
if(!$?) { Exit $LASTEXITCODE }
|
||||
unzip -q snappy-1.2.2.zip
|
||||
if(!$?) { Exit $LASTEXITCODE }
|
||||
cd snappy-1.2.2
|
||||
mkdir build
|
||||
cd build
|
||||
& cmake -G "$Env:CMAKE_GENERATOR" .. -DSNAPPY_BUILD_TESTS=OFF -DSNAPPY_BUILD_BENCHMARKS=OFF
|
||||
if(!$?) { Exit $LASTEXITCODE }
|
||||
cmake --build . --config Debug --parallel 32 -- /p:Platform=x64
|
||||
if(!$?) { Exit $LASTEXITCODE }
|
||||
echo ======================== Build RocksDB =========================
|
||||
cd ${{ github.workspace }}
|
||||
$env:Path = "$env:JAVA_HOME\bin;" + $env:Path
|
||||
mkdir build
|
||||
cd build
|
||||
& cmake -G "$Env:CMAKE_GENERATOR" -DCMAKE_BUILD_TYPE=Debug -DWIN_CI=1 -DPORTABLE="$Env:CMAKE_PORTABLE" -DSNAPPY=1 -DXPRESS=1 -DJNI=1 ..
|
||||
if(!$?) { Exit $LASTEXITCODE }
|
||||
cd ..
|
||||
echo "Building with VS version: $Env:CMAKE_GENERATOR"
|
||||
# use more parallel processes than the number of processes available, as most of the compile command would be cache hit
|
||||
cmake --build build --config Debug --parallel 32 -- /p:LinkIncremental=false /p:Platform=x64
|
||||
if(!$?) { Exit $LASTEXITCODE }
|
||||
echo ========================= Test RocksDB =========================
|
||||
$suiteRun = "${{ inputs.suite-run }}"
|
||||
if ($suiteRun -ne "") {
|
||||
$suiteArray = $suiteRun -split ','
|
||||
build_tools\run_ci_db_test.ps1 -SuiteRun $suiteArray -Concurrency 16
|
||||
if(!$?) { Exit $LASTEXITCODE }
|
||||
} else {
|
||||
echo "Skipping C++ tests (suite-run is empty)"
|
||||
}
|
||||
if ("${{ inputs.run-java }}" -eq "true") {
|
||||
echo ======================== Test RocksJava ========================
|
||||
cd build\java
|
||||
& ctest -C Debug -j 16
|
||||
if(!$?) { Exit $LASTEXITCODE }
|
||||
} else {
|
||||
echo "Skipping Java tests"
|
||||
}
|
||||
shell: pwsh
|
||||
- name: Show ccache stats
|
||||
shell: pwsh
|
||||
run: |
|
||||
ccache --show-stats -v
|
||||
@@ -1,27 +0,0 @@
|
||||
// Shared markdown builder for AI review comment bodies.
|
||||
//
|
||||
// Usage:
|
||||
// const build = require('./build-ai-review-comment.js');
|
||||
// return build({ icon, headerTitle, triggerLine, responseBody, footerLines
|
||||
// });
|
||||
|
||||
module.exports = function buildAiReviewComment(
|
||||
{icon, headerTitle, triggerLine, responseBody, footerLines}) {
|
||||
return [
|
||||
`## ${icon} ${headerTitle}`,
|
||||
'',
|
||||
triggerLine,
|
||||
'',
|
||||
'---',
|
||||
'',
|
||||
responseBody,
|
||||
'',
|
||||
'---',
|
||||
'',
|
||||
'<details>',
|
||||
'<summary>ℹ️ About this response</summary>',
|
||||
'',
|
||||
...footerLines,
|
||||
'</details>',
|
||||
].join('\n');
|
||||
};
|
||||
@@ -1,39 +0,0 @@
|
||||
#!/bin/bash
|
||||
# Trim ccache to keep only entries accessed during the current build.
|
||||
#
|
||||
# Usage:
|
||||
# 1. Before build: touch "$CCACHE_DIR/.build_marker"
|
||||
# 2. Run build (ccache updates mtime on hits, creates new files for misses)
|
||||
# 3. After build: .github/scripts/ccache-trim.sh
|
||||
#
|
||||
# This removes cache files not accessed during the build (stale entries from
|
||||
# previous commits). Only intended for CI where each run builds one commit.
|
||||
# Do NOT use on local builds where multiple worktrees may share the cache.
|
||||
|
||||
set -e
|
||||
|
||||
CCACHE_DIR="${CCACHE_DIR:?CCACHE_DIR must be set}"
|
||||
MARKER="$CCACHE_DIR/.build_marker"
|
||||
|
||||
if [ ! -f "$MARKER" ]; then
|
||||
echo "ccache-trim: No .build_marker found, skipping (was the marker created before build?)"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Count files before cleanup
|
||||
before=$(find "$CCACHE_DIR" -type f \( -name '*R' -o -name '*M' \) | wc -l)
|
||||
|
||||
# Delete cache files (results and manifests) older than the marker
|
||||
find "$CCACHE_DIR" -type f \( -name '*R' -o -name '*M' \) ! -newer "$MARKER" -delete
|
||||
|
||||
# Clean up empty directories
|
||||
find "$CCACHE_DIR" -mindepth 2 -type d -empty -delete 2>/dev/null || true
|
||||
|
||||
# Recalculate size counters
|
||||
ccache -c 2>/dev/null || true
|
||||
|
||||
after=$(find "$CCACHE_DIR" -type f \( -name '*R' -o -name '*M' \) | wc -l)
|
||||
echo "ccache-trim: $before -> $after cache files (removed $((before - after)) stale entries)"
|
||||
|
||||
# Clean up marker
|
||||
rm -f "$MARKER"
|
||||
@@ -1,32 +0,0 @@
|
||||
#!/bin/bash
|
||||
# Compute test shard for parallel CI execution.
|
||||
# Distributes tests round-robin across N shards for balanced load.
|
||||
# Outputs ROCKSDBTESTS_SUBSET — the list of test binaries for this shard.
|
||||
# The Makefile uses this to build and run only the assigned tests.
|
||||
#
|
||||
# Usage: compute-test-shard.sh <shard_index> <num_shards>
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
shard=${1:?Usage: compute-test-shard.sh <shard_index> <num_shards>}
|
||||
nshards=${2:?Usage: compute-test-shard.sh <shard_index> <num_shards>}
|
||||
|
||||
# Get sorted test list (db_test first since it's the heaviest, then alpha)
|
||||
make -s list_all_tests 2>/dev/null | tr ' ' '\n' | grep '_test$' | sort -u > /tmp/sorted.txt
|
||||
(echo db_test; grep -v '^db_test$' /tmp/sorted.txt) > /tmp/all_tests.txt
|
||||
|
||||
total=$(wc -l < /tmp/all_tests.txt)
|
||||
|
||||
# Round-robin: assign test i to shard (i % nshards).
|
||||
# This spreads heavy tests (which are scattered alphabetically) evenly.
|
||||
awk -v s="$shard" -v n="$nshards" 'NR > 0 && (NR - 1) % n == s' /tmp/all_tests.txt > /tmp/include.txt
|
||||
|
||||
included=$(wc -l < /tmp/include.txt)
|
||||
first=$(head -1 /tmp/include.txt)
|
||||
last=$(tail -1 /tmp/include.txt)
|
||||
|
||||
# Output space-separated list for ROCKSDBTESTS_SUBSET
|
||||
subset=$(tr '\n' ' ' < /tmp/include.txt)
|
||||
echo "subset=${subset}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
echo "Shard $shard/$nshards: $included tests (round-robin), first=$first last=$last (total $total)"
|
||||
@@ -1,118 +0,0 @@
|
||||
// Parse Claude Code execution log and produce a markdown review comment.
|
||||
//
|
||||
// Usage from actions/github-script:
|
||||
// const parse = require('./.github/scripts/parse-claude-review.js');
|
||||
// const markdown = parse({ executionFile, conclusion, meta });
|
||||
//
|
||||
// Parameters:
|
||||
// executionFile - path to the JSON execution log from claude-code-base-action
|
||||
// conclusion - 'success' or 'failure' from the action output
|
||||
// meta - { trigger, autoMode, headSha, reviewer, isQuery, isPartial
|
||||
// }
|
||||
|
||||
const fs = require('fs');
|
||||
const buildComment = require('./build-ai-review-comment.js');
|
||||
|
||||
module.exports = function parseClaude({executionFile, conclusion, meta}) {
|
||||
function getTriggerLine() {
|
||||
if (meta.trigger !== 'auto') {
|
||||
return `*Requested by @${meta.reviewer}*`;
|
||||
}
|
||||
const shortSha = meta.headSha ? meta.headSha.substring(0, 7) : 'unknown';
|
||||
if (meta.autoMode === 'early') {
|
||||
return `*Auto-triggered after CI reached the early-review threshold — reviewing commit ${
|
||||
shortSha}*`;
|
||||
}
|
||||
return `*Auto-triggered after CI passed — reviewing commit ${shortSha}*`;
|
||||
}
|
||||
|
||||
let responseBody = '';
|
||||
|
||||
try {
|
||||
const executionLog = JSON.parse(fs.readFileSync(executionFile, 'utf8'));
|
||||
|
||||
if (!Array.isArray(executionLog)) {
|
||||
throw new Error('Expected array format from claude-code-base-action');
|
||||
}
|
||||
|
||||
const resultMessage = executionLog.find(m => m.type === 'result');
|
||||
|
||||
// Helper: extract the last substantial assistant text from the log.
|
||||
// Used as a fallback when Claude ran out of turns and the recovery
|
||||
// session also failed to produce a result.
|
||||
function getLastAssistantText(log, minLength = 200) {
|
||||
for (let i = log.length - 1; i >= 0; i--) {
|
||||
const m = log[i];
|
||||
if (m.type !== 'assistant') continue;
|
||||
const content = m.message && m.message.content;
|
||||
if (!Array.isArray(content)) continue;
|
||||
for (let j = content.length - 1; j >= 0; j--) {
|
||||
if (content[j].type === 'text' && content[j].text &&
|
||||
content[j].text.trim().length >= minLength) {
|
||||
const text = content[j].text.trim();
|
||||
// Truncate to avoid enormous PR comments
|
||||
return text.length > 50000 ? text.substring(0, 50000) +
|
||||
'\n\n*[Truncated — full output in execution log artifact]*' :
|
||||
text;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!resultMessage) {
|
||||
responseBody = '⚠️ No result message found in execution log.';
|
||||
} else if (resultMessage.subtype === 'success' && resultMessage.result) {
|
||||
responseBody = resultMessage.result;
|
||||
} else if (resultMessage.is_error || resultMessage.subtype === 'error') {
|
||||
const errorInfo =
|
||||
resultMessage.result || resultMessage.error || 'Unknown error';
|
||||
responseBody = `❌ **Claude encountered an error:**\n\n${errorInfo}`;
|
||||
} else if (resultMessage.subtype === 'error_max_turns') {
|
||||
// The workflow runs a recovery session when this happens, so this
|
||||
// branch is typically only hit if recovery wasn't attempted (e.g.,
|
||||
// no findings file was written). Extract what we can.
|
||||
const partial = getLastAssistantText(executionLog);
|
||||
if (partial) {
|
||||
responseBody =
|
||||
`⚠️ **Review incomplete — Claude hit the turn limit.**\n\nBelow is the last partial output. You can request a fresh review with \`/claude-review\`.\n\n---\n\n${
|
||||
partial}`;
|
||||
} else {
|
||||
responseBody =
|
||||
'⚠️ **Review incomplete — Claude hit the turn limit before producing output.** You can request a fresh review with `/claude-review`.';
|
||||
}
|
||||
} else if (resultMessage.result) {
|
||||
responseBody = `⚠️ **Completed with status: ${
|
||||
resultMessage.subtype}**\n\n${resultMessage.result}`;
|
||||
} else {
|
||||
responseBody = '⚠️ Claude completed but produced no output.';
|
||||
}
|
||||
} catch (error) {
|
||||
responseBody = `❌ Error parsing Claude response: ${error.message}`;
|
||||
}
|
||||
|
||||
const isPartial = !!meta.isPartial;
|
||||
const icon = isPartial ? '🟡' : (conclusion === 'success' ? '✅' : '⚠️');
|
||||
const headerTitle = meta.isQuery ? 'Claude Response' : 'Claude Code Review';
|
||||
const triggerLine = getTriggerLine();
|
||||
|
||||
return buildComment({
|
||||
icon,
|
||||
headerTitle,
|
||||
triggerLine,
|
||||
responseBody,
|
||||
footerLines: [
|
||||
'Generated by [Claude Code](https://github.com/anthropics/claude-code-action).',
|
||||
'Review methodology: `claude_md/code_review.md`',
|
||||
'',
|
||||
'**Limitations:**',
|
||||
'- Claude may miss context from files not in the diff',
|
||||
'- Large PRs may be truncated',
|
||||
'- Always apply human judgment to AI suggestions',
|
||||
'',
|
||||
'**Commands:**',
|
||||
'- `/claude-review [context]` — Request a code review',
|
||||
'- `/claude-query <question>` — Ask about the PR or codebase',
|
||||
],
|
||||
});
|
||||
};
|
||||
@@ -1,98 +0,0 @@
|
||||
// Parse Codex review artifacts and produce a markdown review comment.
|
||||
//
|
||||
// Usage from actions/github-script:
|
||||
// const parse = require('./.github/scripts/parse-codex-review.js');
|
||||
// const markdown = parse({ responseFile, recoveryFile, findingsFile, logFile,
|
||||
// exitCode, meta });
|
||||
//
|
||||
// Parameters:
|
||||
// responseFile - path to Codex final response output
|
||||
// recoveryFile - path to recovery output formatted from review-findings.md
|
||||
// findingsFile - path to incremental findings file written during review
|
||||
// logFile - path to Codex stdout/stderr log
|
||||
// exitCode - Codex process exit code
|
||||
// meta - { trigger, autoMode, headSha, reviewer, isQuery, isPartial }
|
||||
|
||||
const fs = require('fs');
|
||||
const buildComment = require('./build-ai-review-comment.js');
|
||||
|
||||
module.exports = function parseCodex(
|
||||
{responseFile, recoveryFile, findingsFile, logFile, exitCode, meta}) {
|
||||
function getTriggerLine() {
|
||||
if (meta.trigger !== 'auto') {
|
||||
return `*Requested by @${meta.reviewer}*`;
|
||||
}
|
||||
const shortSha = meta.headSha ? meta.headSha.substring(0, 7) : 'unknown';
|
||||
if (meta.autoMode === 'early') {
|
||||
return `*Auto-triggered after CI reached the early-review threshold — reviewing commit ${
|
||||
shortSha}*`;
|
||||
}
|
||||
return `*Auto-triggered after CI passed — reviewing commit ${shortSha}*`;
|
||||
}
|
||||
|
||||
function readIfPresent(path) {
|
||||
if (!path || !fs.existsSync(path)) {
|
||||
return '';
|
||||
}
|
||||
const text = fs.readFileSync(path, 'utf8').trim();
|
||||
return text;
|
||||
}
|
||||
|
||||
function tailFile(path, maxChars = 12000) {
|
||||
const text = readIfPresent(path);
|
||||
if (!text) {
|
||||
return '';
|
||||
}
|
||||
if (text.length <= maxChars) {
|
||||
return text;
|
||||
}
|
||||
return text.slice(text.length - maxChars);
|
||||
}
|
||||
|
||||
let responseBody = '';
|
||||
const recovered = readIfPresent(recoveryFile);
|
||||
const direct = readIfPresent(responseFile);
|
||||
const findings = readIfPresent(findingsFile);
|
||||
|
||||
if (recovered) {
|
||||
responseBody = recovered;
|
||||
} else if (direct) {
|
||||
responseBody = direct;
|
||||
} else if (findings) {
|
||||
responseBody =
|
||||
'⚠️ **Review incomplete — Codex did not produce a final response.**\n\n' +
|
||||
'Below are the incremental findings recovered from `review-findings.md`.\n\n---\n\n' +
|
||||
findings;
|
||||
} else {
|
||||
const logTail = tailFile(logFile);
|
||||
responseBody = logTail ?
|
||||
`❌ **Codex review failed before producing findings.**\n\n\`\`\`\n${
|
||||
logTail}\n\`\`\`` :
|
||||
'❌ Codex review failed before producing any output.';
|
||||
}
|
||||
|
||||
const isPartial = !!meta.isPartial;
|
||||
const code = Number.parseInt(exitCode || '1', 10);
|
||||
const icon = isPartial ? '🟡' : (code === 0 ? '✅' : '⚠️');
|
||||
const triggerLine = getTriggerLine();
|
||||
|
||||
return buildComment({
|
||||
icon,
|
||||
headerTitle: meta.isQuery ? 'Codex Response' : 'Codex Code Review',
|
||||
triggerLine,
|
||||
responseBody,
|
||||
footerLines: [
|
||||
'Generated by Codex CLI.',
|
||||
'Review methodology: `claude_md/code_review.md`',
|
||||
'',
|
||||
'**Limitations:**',
|
||||
'- Codex may miss context from files not in the diff',
|
||||
'- Large PRs may be truncated',
|
||||
'- Always apply human judgment to AI suggestions',
|
||||
'',
|
||||
'**Commands:**',
|
||||
'- `/codex-review [context]` — Request a code review',
|
||||
'- `/codex-query <question>` — Ask about the PR or codebase',
|
||||
],
|
||||
});
|
||||
};
|
||||
@@ -1,204 +0,0 @@
|
||||
// Shared PR comment posting utility.
|
||||
// Used by both clang-tidy-comment.yml and claude-review-comment.yml.
|
||||
//
|
||||
// Usage from actions/github-script:
|
||||
// const post = require('./.github/scripts/post-pr-comment.js');
|
||||
// await post({ github, context, core, prNumber, body, marker });
|
||||
//
|
||||
// Parameters:
|
||||
// github - octokit instance from actions/github-script
|
||||
// context - GitHub Actions context
|
||||
// core - @actions/core for logging
|
||||
// prNumber - PR number to comment on
|
||||
// body - comment body (markdown string)
|
||||
// marker - HTML comment marker for dedup (e.g. '<!-- claude-review -->')
|
||||
// If an existing comment with this marker is found, it is updated.
|
||||
// If not found, a new comment is created.
|
||||
// legacyMarkers - optional list of legacy markers that should be migrated by
|
||||
// being considered part of the same comment family.
|
||||
// prunePrefix - optional marker prefix whose older comments should be
|
||||
// superseded. If obsoleteTitle is set, older comments are
|
||||
// collapsed instead of deleted.
|
||||
// preserveLatest - optional count of active comments to keep when
|
||||
// prunePrefix is set.
|
||||
// obsoleteMarker - optional HTML marker used to detect already-obsolete
|
||||
// comments.
|
||||
// obsoleteTitle - optional heading to use when collapsing superseded
|
||||
// comments into a details block.
|
||||
|
||||
module.exports = async function postPrComment({
|
||||
github,
|
||||
context,
|
||||
core,
|
||||
prNumber,
|
||||
body,
|
||||
marker,
|
||||
legacyMarkers = [],
|
||||
prunePrefix = '',
|
||||
preserveLatest = 0,
|
||||
obsoleteMarker = '',
|
||||
obsoleteTitle = '',
|
||||
}) {
|
||||
if (!prNumber || !body) {
|
||||
core.warning('Missing prNumber or body; skipping comment.');
|
||||
return;
|
||||
}
|
||||
|
||||
const owner = context.repo.owner;
|
||||
const repo = context.repo.repo;
|
||||
|
||||
// Ensure marker is embedded in the body
|
||||
const markedBody = body.includes(marker) ? body : `${marker}\n${body}`;
|
||||
|
||||
async function listComments() {
|
||||
return await github.paginate(github.rest.issues.listComments, {
|
||||
owner,
|
||||
repo,
|
||||
issue_number: prNumber,
|
||||
per_page: 100,
|
||||
});
|
||||
}
|
||||
|
||||
function commentActivityTime(comment) {
|
||||
const timestamp =
|
||||
Date.parse(comment.updated_at || comment.created_at || '');
|
||||
return Number.isNaN(timestamp) ? 0 : timestamp;
|
||||
}
|
||||
|
||||
function commentSortDescending(left, right) {
|
||||
const timeDelta = commentActivityTime(right) - commentActivityTime(left);
|
||||
if (timeDelta !== 0) {
|
||||
return timeDelta;
|
||||
}
|
||||
return Number(right.id || 0) - Number(left.id || 0);
|
||||
}
|
||||
|
||||
function isObsoleteComment(comment) {
|
||||
return !!obsoleteMarker && typeof comment.body === 'string' &&
|
||||
comment.body.includes(obsoleteMarker);
|
||||
}
|
||||
|
||||
function buildObsoleteBody(comment) {
|
||||
const originalBody =
|
||||
typeof comment.body === 'string' && comment.body.trim() ?
|
||||
comment.body :
|
||||
'*No original review body preserved.*';
|
||||
const title = obsoleteTitle || 'AI Review - OBSOLETE';
|
||||
return `${obsoleteMarker}\n## ${
|
||||
title}\n\n<details>\n<summary>Superseded by a newer AI review. Expand to see the original review.</summary>\n\n${
|
||||
originalBody}\n\n</details>`;
|
||||
}
|
||||
|
||||
async function deleteCommentIfPresent(comment, reason) {
|
||||
try {
|
||||
await github.rest.issues.deleteComment({
|
||||
owner,
|
||||
repo,
|
||||
comment_id: comment.id,
|
||||
});
|
||||
core.info(`${reason} ${comment.id}`);
|
||||
} catch (error) {
|
||||
if (error.status === 404) {
|
||||
core.info(`Comment ${comment.id} was already deleted by another run.`);
|
||||
return;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async function supersedeCommentIfPresent(comment, reason) {
|
||||
if (!obsoleteTitle) {
|
||||
await deleteCommentIfPresent(comment, reason);
|
||||
return;
|
||||
}
|
||||
if (isObsoleteComment(comment)) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await github.rest.issues.updateComment({
|
||||
owner,
|
||||
repo,
|
||||
comment_id: comment.id,
|
||||
body: buildObsoleteBody(comment),
|
||||
});
|
||||
core.info(`${reason} ${comment.id}`);
|
||||
} catch (error) {
|
||||
if (error.status === 404) {
|
||||
core.info(`Comment ${comment.id} was already deleted by another run.`);
|
||||
return;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
let comments = await listComments();
|
||||
const existing = comments.find(
|
||||
comment =>
|
||||
typeof comment.body === 'string' && comment.body.includes(marker));
|
||||
let currentCommentId = null;
|
||||
|
||||
if (existing) {
|
||||
try {
|
||||
const response = await github.rest.issues.updateComment({
|
||||
owner,
|
||||
repo,
|
||||
comment_id: existing.id,
|
||||
body: markedBody,
|
||||
});
|
||||
currentCommentId = existing.id;
|
||||
core.info(`Updated existing comment ${existing.id}`);
|
||||
} catch (error) {
|
||||
if (error.status !== 404) {
|
||||
throw error;
|
||||
}
|
||||
core.info(
|
||||
`Comment ${existing.id} disappeared before update; ` +
|
||||
'creating a fresh comment instead.');
|
||||
}
|
||||
}
|
||||
|
||||
if (!currentCommentId) {
|
||||
const response = await github.rest.issues.createComment({
|
||||
owner,
|
||||
repo,
|
||||
issue_number: prNumber,
|
||||
body: markedBody,
|
||||
});
|
||||
currentCommentId = response.data.id;
|
||||
core.info('Created new PR comment');
|
||||
}
|
||||
|
||||
if (prunePrefix || legacyMarkers.length > 0) {
|
||||
comments = await listComments();
|
||||
const relatedComments = comments.filter(
|
||||
comment => comment.id !== currentCommentId &&
|
||||
typeof comment.body === 'string' &&
|
||||
((prunePrefix && comment.body.includes(prunePrefix)) ||
|
||||
legacyMarkers.some(
|
||||
legacyMarker => comment.body.includes(legacyMarker))));
|
||||
const activeRelatedComments =
|
||||
comments
|
||||
.filter(
|
||||
comment => typeof comment.body === 'string' &&
|
||||
!isObsoleteComment(comment) &&
|
||||
(comment.id === currentCommentId ||
|
||||
relatedComments.some(
|
||||
relatedComment => relatedComment.id === comment.id)))
|
||||
.sort(commentSortDescending);
|
||||
|
||||
const keep =
|
||||
new Set(activeRelatedComments.slice(0, Math.max(preserveLatest, 1))
|
||||
.map(comment => comment.id));
|
||||
|
||||
const supersedeCandidates = obsoleteTitle ?
|
||||
activeRelatedComments.filter(comment => !keep.has(comment.id)) :
|
||||
relatedComments.filter(comment => !keep.has(comment.id));
|
||||
|
||||
for (const comment of supersedeCandidates) {
|
||||
if (keep.has(comment.id)) {
|
||||
continue;
|
||||
}
|
||||
await supersedeCommentIfPresent(comment, 'Superseded old AI review');
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -1,347 +0,0 @@
|
||||
const test = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
|
||||
const postPrComment = require('./post-pr-comment.js');
|
||||
|
||||
const OBSOLETE_MARKER = '<!-- claude-review-obsolete -->';
|
||||
const OBSOLETE_TITLE = 'Claude Code Review - OBSOLETE';
|
||||
|
||||
function makeComment(id, body, createdAt, updatedAt) {
|
||||
return {
|
||||
id,
|
||||
body,
|
||||
created_at: createdAt,
|
||||
updated_at: updatedAt || createdAt,
|
||||
};
|
||||
}
|
||||
|
||||
function createHarness(initialComments, options = {}) {
|
||||
let comments = initialComments.map(comment => ({...comment}));
|
||||
let nextCommentId = options.nextCommentId || 1000;
|
||||
let paginateCount = 0;
|
||||
|
||||
const calls = {
|
||||
update: [],
|
||||
create: [],
|
||||
delete: [],
|
||||
};
|
||||
|
||||
const github = {
|
||||
paginate: async () => {
|
||||
paginateCount++;
|
||||
if (options.onPaginate) {
|
||||
const updated = options.onPaginate({
|
||||
paginateCount,
|
||||
comments: comments.map(comment => ({...comment})),
|
||||
});
|
||||
if (updated) {
|
||||
comments = updated.map(comment => ({...comment}));
|
||||
}
|
||||
}
|
||||
return comments.map(comment => ({...comment}));
|
||||
},
|
||||
rest: {
|
||||
issues: {
|
||||
listComments: () => {
|
||||
throw new Error('listComments should only be used through paginate');
|
||||
},
|
||||
updateComment: async ({comment_id, body}) => {
|
||||
calls.update.push({comment_id, body});
|
||||
const error =
|
||||
options.updateErrors && options.updateErrors[comment_id];
|
||||
if (error) {
|
||||
throw error;
|
||||
}
|
||||
const index =
|
||||
comments.findIndex(comment => comment.id === comment_id);
|
||||
if (index === -1) {
|
||||
const notFound = new Error(`Comment ${comment_id} not found`);
|
||||
notFound.status = 404;
|
||||
throw notFound;
|
||||
}
|
||||
const updated = {
|
||||
...comments[index],
|
||||
body,
|
||||
updated_at: options.updateTimestamp || '2026-04-24T00:00:00Z',
|
||||
};
|
||||
comments[index] = updated;
|
||||
return {data: {...updated}};
|
||||
},
|
||||
createComment: async ({issue_number, body}) => {
|
||||
calls.create.push({issue_number, body});
|
||||
const created = makeComment(
|
||||
nextCommentId++, body,
|
||||
options.createTimestamp || '2026-04-24T00:00:00Z');
|
||||
comments.push(created);
|
||||
return {data: {id: created.id}};
|
||||
},
|
||||
deleteComment: async ({comment_id}) => {
|
||||
calls.delete.push({comment_id});
|
||||
const error =
|
||||
options.deleteErrors && options.deleteErrors[comment_id];
|
||||
if (error) {
|
||||
throw error;
|
||||
}
|
||||
comments = comments.filter(comment => comment.id !== comment_id);
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
return {
|
||||
github,
|
||||
calls,
|
||||
getComments: () => comments.map(comment => ({...comment})),
|
||||
};
|
||||
}
|
||||
|
||||
function createCore() {
|
||||
return {
|
||||
info: () => {},
|
||||
warning: () => {},
|
||||
};
|
||||
}
|
||||
|
||||
function escapeRegExp(value) {
|
||||
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||||
}
|
||||
|
||||
function assertObsoleteComment(comment, originalBody) {
|
||||
assert.ok(comment);
|
||||
assert.match(comment.body, new RegExp(escapeRegExp(OBSOLETE_MARKER)));
|
||||
assert.match(comment.body, new RegExp(`## ${escapeRegExp(OBSOLETE_TITLE)}`));
|
||||
assert.match(comment.body, /<details>/);
|
||||
assert.match(comment.body, /Superseded by a newer AI review/);
|
||||
assert.match(comment.body, new RegExp(escapeRegExp(originalBody)));
|
||||
}
|
||||
|
||||
const context = {
|
||||
repo: {
|
||||
owner: 'facebook',
|
||||
repo: 'rocksdb',
|
||||
},
|
||||
};
|
||||
|
||||
test(
|
||||
'creates a fresh review comment and supersedes legacy comments',
|
||||
async () => {
|
||||
const harness = createHarness(
|
||||
[
|
||||
makeComment(
|
||||
1, '<!-- claude-review-auto -->\nold legacy', 'not-a-date'),
|
||||
makeComment(
|
||||
2, '<!-- claude-review-auto -->\nnew legacy',
|
||||
'2026-04-21T00:00:00Z'),
|
||||
],
|
||||
{
|
||||
updateTimestamp: '2026-04-24T00:00:00Z',
|
||||
});
|
||||
|
||||
await postPrComment({
|
||||
github: harness.github,
|
||||
context,
|
||||
core: createCore(),
|
||||
prNumber: 14659,
|
||||
body: 'review body',
|
||||
marker: '<!-- claude-review-auto-run-500 -->',
|
||||
legacyMarkers: ['<!-- claude-review-auto -->'],
|
||||
prunePrefix: '<!-- claude-review-auto-',
|
||||
preserveLatest: 1,
|
||||
obsoleteMarker: OBSOLETE_MARKER,
|
||||
obsoleteTitle: OBSOLETE_TITLE,
|
||||
});
|
||||
|
||||
assert.equal(harness.calls.create.length, 1);
|
||||
assert.deepEqual(
|
||||
harness.calls.update.map(call => call.comment_id), [2, 1]);
|
||||
assert.equal(harness.calls.delete.length, 0);
|
||||
|
||||
const comments = harness.getComments();
|
||||
assert.equal(comments.length, 3);
|
||||
assert.match(
|
||||
comments.find(comment => comment.id === 1000).body,
|
||||
/<!-- claude-review-auto-run-500 -->/);
|
||||
assertObsoleteComment(
|
||||
comments.find(comment => comment.id === 2),
|
||||
'<!-- claude-review-auto -->\nnew legacy');
|
||||
assertObsoleteComment(
|
||||
comments.find(comment => comment.id === 1),
|
||||
'<!-- claude-review-auto -->\nold legacy');
|
||||
});
|
||||
|
||||
test(
|
||||
'updates an exact-match comment and supersedes leftover legacy comments',
|
||||
async () => {
|
||||
const harness = createHarness(
|
||||
[
|
||||
makeComment(
|
||||
3, '<!-- claude-review-auto-abcdef0 -->\ncurrent body',
|
||||
'2026-04-24T00:00:00Z'),
|
||||
makeComment(
|
||||
4, '<!-- claude-review-auto -->\nlegacy body',
|
||||
'2026-04-20T00:00:00Z'),
|
||||
],
|
||||
{
|
||||
updateTimestamp: '2026-04-24T01:00:00Z',
|
||||
});
|
||||
|
||||
await postPrComment({
|
||||
github: harness.github,
|
||||
context,
|
||||
core: createCore(),
|
||||
prNumber: 14659,
|
||||
body: 'refreshed body',
|
||||
marker: '<!-- claude-review-auto-abcdef0 -->',
|
||||
legacyMarkers: ['<!-- claude-review-auto -->'],
|
||||
prunePrefix: '<!-- claude-review-auto-',
|
||||
preserveLatest: 1,
|
||||
obsoleteMarker: OBSOLETE_MARKER,
|
||||
obsoleteTitle: OBSOLETE_TITLE,
|
||||
});
|
||||
|
||||
assert.deepEqual(
|
||||
harness.calls.update.map(call => call.comment_id), [3, 4]);
|
||||
assert.equal(harness.calls.create.length, 0);
|
||||
assert.equal(harness.calls.delete.length, 0);
|
||||
|
||||
const comments = harness.getComments();
|
||||
assert.match(
|
||||
comments.find(comment => comment.id === 3).body,
|
||||
/<!-- claude-review-auto-abcdef0 -->\nrefreshed body/);
|
||||
assertObsoleteComment(
|
||||
comments.find(comment => comment.id === 4),
|
||||
'<!-- claude-review-auto -->\nlegacy body');
|
||||
});
|
||||
|
||||
test(
|
||||
'creates a new comment if the target disappears before update',
|
||||
async () => {
|
||||
const missing = new Error('gone');
|
||||
missing.status = 404;
|
||||
const harness = createHarness(
|
||||
[
|
||||
makeComment(
|
||||
10, '<!-- claude-review-auto-abcdef0 -->\nold body',
|
||||
'2026-04-20T00:00:00Z'),
|
||||
],
|
||||
{
|
||||
updateErrors: {
|
||||
10: missing,
|
||||
},
|
||||
});
|
||||
|
||||
await postPrComment({
|
||||
github: harness.github,
|
||||
context,
|
||||
core: createCore(),
|
||||
prNumber: 14659,
|
||||
body: 'replacement body',
|
||||
marker: '<!-- claude-review-auto-abcdef0 -->',
|
||||
});
|
||||
|
||||
assert.equal(harness.calls.update.length, 1);
|
||||
assert.equal(harness.calls.create.length, 1);
|
||||
assert.match(
|
||||
harness.calls.create[0].body, /<!-- claude-review-auto-abcdef0 -->/);
|
||||
});
|
||||
|
||||
test(
|
||||
'ignores 404 when superseding a comment already deleted by another run',
|
||||
async () => {
|
||||
const missing = new Error('gone');
|
||||
missing.status = 404;
|
||||
const harness = createHarness(
|
||||
[
|
||||
makeComment(
|
||||
20, '<!-- claude-review-auto-oldest -->\noldest',
|
||||
'2026-04-20T00:00:00Z'),
|
||||
makeComment(
|
||||
21, '<!-- claude-review-auto-newer -->\nnewer',
|
||||
'2026-04-21T00:00:00Z'),
|
||||
],
|
||||
{
|
||||
nextCommentId: 30,
|
||||
createTimestamp: '2026-04-24T00:00:00Z',
|
||||
deleteErrors: {
|
||||
20: missing,
|
||||
},
|
||||
});
|
||||
|
||||
await postPrComment({
|
||||
github: harness.github,
|
||||
context,
|
||||
core: createCore(),
|
||||
prNumber: 14659,
|
||||
body: 'fresh body',
|
||||
marker: '<!-- claude-review-auto-latest -->',
|
||||
prunePrefix: '<!-- claude-review-auto-',
|
||||
preserveLatest: 1,
|
||||
obsoleteMarker: OBSOLETE_MARKER,
|
||||
obsoleteTitle: OBSOLETE_TITLE,
|
||||
});
|
||||
|
||||
assert.equal(harness.calls.create.length, 1);
|
||||
assert.equal(harness.calls.delete.length, 0);
|
||||
assert.deepEqual(
|
||||
harness.calls.update.map(call => call.comment_id), [21, 20]);
|
||||
|
||||
const comments = harness.getComments();
|
||||
assertObsoleteComment(
|
||||
comments.find(comment => comment.id === 21),
|
||||
'<!-- claude-review-auto-newer -->\nnewer');
|
||||
});
|
||||
|
||||
test(
|
||||
'supersedes the current review when a newer concurrent one appears',
|
||||
async () => {
|
||||
const harness = createHarness(
|
||||
[
|
||||
makeComment(
|
||||
40, '<!-- claude-review-auto-current -->\ncurrent',
|
||||
'2026-04-20T00:00:00Z'),
|
||||
makeComment(
|
||||
41, '<!-- claude-review-auto-older -->\nolder',
|
||||
'2026-04-19T00:00:00Z'),
|
||||
],
|
||||
{
|
||||
updateTimestamp: '2026-04-24T00:00:00Z',
|
||||
onPaginate: ({paginateCount, comments}) => {
|
||||
if (paginateCount !== 2) {
|
||||
return comments;
|
||||
}
|
||||
return comments.concat([
|
||||
makeComment(
|
||||
42, '<!-- claude-review-auto-newer -->\nnewer',
|
||||
'2026-04-25T00:00:00Z'),
|
||||
]);
|
||||
},
|
||||
});
|
||||
|
||||
await postPrComment({
|
||||
github: harness.github,
|
||||
context,
|
||||
core: createCore(),
|
||||
prNumber: 14659,
|
||||
body: 'updated current body',
|
||||
marker: '<!-- claude-review-auto-current -->',
|
||||
prunePrefix: '<!-- claude-review-auto-',
|
||||
preserveLatest: 1,
|
||||
obsoleteMarker: OBSOLETE_MARKER,
|
||||
obsoleteTitle: OBSOLETE_TITLE,
|
||||
});
|
||||
|
||||
assert.deepEqual(
|
||||
harness.calls.update.map(call => call.comment_id), [40, 40, 41]);
|
||||
assert.equal(harness.calls.delete.length, 0);
|
||||
|
||||
const comments = harness.getComments();
|
||||
assert.match(
|
||||
comments.find(comment => comment.id === 42).body,
|
||||
/<!-- claude-review-auto-newer -->\nnewer/);
|
||||
assertObsoleteComment(
|
||||
comments.find(comment => comment.id === 40),
|
||||
'<!-- claude-review-auto-current -->\nupdated current body');
|
||||
assertObsoleteComment(
|
||||
comments.find(comment => comment.id === 41),
|
||||
'<!-- claude-review-auto-older -->\nolder');
|
||||
});
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,185 +0,0 @@
|
||||
# Shared comment-posting workflow for AI reviews.
|
||||
|
||||
name: AI Review Comment
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
provider:
|
||||
description: Provider key, for example "claude" or "codex"
|
||||
required: true
|
||||
type: string
|
||||
result_artifact_name:
|
||||
description: Artifact name produced by the analysis workflow
|
||||
required: true
|
||||
type: string
|
||||
comment_file:
|
||||
description: Markdown comment file produced by the analysis workflow
|
||||
required: true
|
||||
type: string
|
||||
|
||||
permissions:
|
||||
pull-requests: write
|
||||
issues: write
|
||||
|
||||
jobs:
|
||||
comment:
|
||||
if: github.event.workflow_run.conclusion == 'success'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout scripts
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
sparse-checkout: .github/scripts
|
||||
sparse-checkout-cone-mode: true
|
||||
|
||||
- name: Download review artifact
|
||||
id: download
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: ${{ inputs.result_artifact_name }}
|
||||
run-id: ${{ github.event.workflow_run.id }}
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
continue-on-error: true
|
||||
|
||||
- name: Post or update PR comment
|
||||
if: steps.download.outcome == 'success'
|
||||
env:
|
||||
PROVIDER: ${{ inputs.provider }}
|
||||
COMMENT_FILE: ${{ inputs.comment_file }}
|
||||
RUN_ID: ${{ github.event.workflow_run.id }}
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
const fs = require('fs');
|
||||
if (!fs.existsSync(process.env.COMMENT_FILE) ||
|
||||
!fs.existsSync('pr_number.txt')) {
|
||||
core.info('No review results found; skipping.');
|
||||
return;
|
||||
}
|
||||
|
||||
const post = require('./.github/scripts/post-pr-comment.js');
|
||||
const provider = process.env.PROVIDER;
|
||||
const providerTitle = provider === 'codex' ? 'Codex' : 'Claude';
|
||||
const body = fs.readFileSync(process.env.COMMENT_FILE, 'utf8');
|
||||
const prNumber = parseInt(
|
||||
fs.readFileSync('pr_number.txt', 'utf8').trim(),
|
||||
10
|
||||
);
|
||||
const trigger = fs.existsSync('trigger_type.txt') ?
|
||||
fs.readFileSync('trigger_type.txt', 'utf8').trim() :
|
||||
'auto';
|
||||
const headSha = fs.existsSync('head_sha.txt') ?
|
||||
fs.readFileSync('head_sha.txt', 'utf8').trim() :
|
||||
'';
|
||||
const shortSha = headSha ? headSha.substring(0, 7) : '';
|
||||
const runId = process.env.RUN_ID;
|
||||
|
||||
let marker = '';
|
||||
let legacyMarkers = [];
|
||||
let prunePrefix = '';
|
||||
let preserveLatest = 0;
|
||||
let obsoleteMarker = '';
|
||||
let obsoleteTitle = '';
|
||||
|
||||
if (trigger === 'auto') {
|
||||
if (!shortSha) {
|
||||
core.warning(
|
||||
'Missing head_sha.txt for auto review; skipping comment.'
|
||||
);
|
||||
return;
|
||||
}
|
||||
marker = `<!-- ${provider}-review-auto-run-${runId} -->`;
|
||||
legacyMarkers = [`<!-- ${provider}-review-auto -->`];
|
||||
prunePrefix = `<!-- ${provider}-review-auto-`;
|
||||
preserveLatest = 1;
|
||||
obsoleteMarker = `<!-- ${provider}-review-obsolete -->`;
|
||||
obsoleteTitle = `${providerTitle} Code Review - OBSOLETE`;
|
||||
} else {
|
||||
marker = `<!-- ${provider}-review-manual-run-${runId} -->`;
|
||||
}
|
||||
|
||||
await post({
|
||||
github,
|
||||
context,
|
||||
core,
|
||||
prNumber,
|
||||
body,
|
||||
marker,
|
||||
legacyMarkers,
|
||||
prunePrefix,
|
||||
preserveLatest,
|
||||
obsoleteMarker,
|
||||
obsoleteTitle,
|
||||
});
|
||||
|
||||
- name: Add reaction to trigger comment
|
||||
if: steps.download.outcome == 'success'
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
const fs = require('fs');
|
||||
if (!fs.existsSync('trigger_type.txt')) return;
|
||||
const trigger = fs.readFileSync('trigger_type.txt', 'utf8').trim();
|
||||
if (trigger !== 'manual') return;
|
||||
if (!fs.existsSync('comment_id.txt')) return;
|
||||
const commentId = parseInt(
|
||||
fs.readFileSync('comment_id.txt', 'utf8').trim(),
|
||||
10
|
||||
);
|
||||
if (!commentId) return;
|
||||
|
||||
await github.rest.reactions.createForIssueComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
comment_id: commentId,
|
||||
content: 'rocket'
|
||||
});
|
||||
|
||||
failure-notice:
|
||||
if: github.event.workflow_run.conclusion == 'failure'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Download artifact for PR number
|
||||
id: download
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: ${{ inputs.result_artifact_name }}
|
||||
run-id: ${{ github.event.workflow_run.id }}
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
continue-on-error: true
|
||||
|
||||
- name: React with failure
|
||||
if: steps.download.outcome == 'success'
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
const fs = require('fs');
|
||||
if (!fs.existsSync('comment_id.txt')) return;
|
||||
const commentId = parseInt(
|
||||
fs.readFileSync('comment_id.txt', 'utf8').trim(),
|
||||
10
|
||||
);
|
||||
if (!commentId) return;
|
||||
|
||||
await github.rest.reactions.createForIssueComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
comment_id: commentId,
|
||||
content: 'confused'
|
||||
});
|
||||
|
||||
unauthorized-notice:
|
||||
runs-on: ubuntu-latest
|
||||
if: >-
|
||||
github.event.workflow_run.event == 'issue_comment' &&
|
||||
github.event.workflow_run.conclusion == 'skipped'
|
||||
steps:
|
||||
- name: Log skipped unauthorized request
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
core.info(
|
||||
'Analysis workflow was skipped, which usually means the ' +
|
||||
'issue_comment requester was not in the authorized list.'
|
||||
);
|
||||
@@ -1,15 +0,0 @@
|
||||
name: facebook/rocksdb/benchmark-linux
|
||||
on: workflow_dispatch
|
||||
permissions: {}
|
||||
# FIXME: Disabled temporarily
|
||||
# schedule:
|
||||
# - cron: 7 */2 * * * # At minute 7 past every 2nd hour
|
||||
jobs:
|
||||
benchmark-linux:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on: ubuntu-latest # FIXME: change this back to self-hosted when ready
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/build-for-benchmarks"
|
||||
- uses: "./.github/actions/perform-benchmarks"
|
||||
- uses: "./.github/actions/post-benchmarks"
|
||||
@@ -1,51 +0,0 @@
|
||||
name: Post clang-tidy PR comment
|
||||
on:
|
||||
workflow_run:
|
||||
workflows: ["clang-tidy"]
|
||||
types: [completed]
|
||||
|
||||
permissions:
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
comment:
|
||||
if: github.event.workflow_run.event == 'pull_request'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout scripts
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
sparse-checkout: .github/scripts
|
||||
sparse-checkout-cone-mode: true
|
||||
|
||||
- name: Download clang-tidy results
|
||||
id: download
|
||||
uses: actions/download-artifact@v4.1.3
|
||||
with:
|
||||
name: clang-tidy-result
|
||||
run-id: ${{ github.event.workflow_run.id }}
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
continue-on-error: true
|
||||
|
||||
- name: Post or update PR comment
|
||||
if: steps.download.outcome == 'success'
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
const fs = require('fs');
|
||||
if (!fs.existsSync('clang-tidy-comment.md') || !fs.existsSync('pr_number.txt')) {
|
||||
core.info('No clang-tidy results found; skipping.');
|
||||
return;
|
||||
}
|
||||
const body = fs.readFileSync('clang-tidy-comment.md', 'utf8');
|
||||
const prNumber = parseInt(fs.readFileSync('pr_number.txt', 'utf8').trim());
|
||||
const post = require('./.github/scripts/post-pr-comment.js');
|
||||
|
||||
await post({
|
||||
github,
|
||||
context,
|
||||
core,
|
||||
prNumber,
|
||||
body,
|
||||
marker: '<!-- clang-tidy-bot -->',
|
||||
});
|
||||
@@ -1,74 +0,0 @@
|
||||
name: clang-tidy
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
clang-tidy:
|
||||
if: github.repository_owner == 'facebook'
|
||||
runs-on:
|
||||
labels: 4-core-ubuntu
|
||||
container:
|
||||
image: ghcr.io/facebook/rocksdb_ubuntu:24.1
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
with:
|
||||
fetch-depth: 2
|
||||
- name: Mark workspace as safe for git
|
||||
run: git config --global --add safe.directory $GITHUB_WORKSPACE
|
||||
- name: Determine diff base
|
||||
id: diff-base
|
||||
run: |
|
||||
if [ "${{ github.event_name }}" = "pull_request" ]; then
|
||||
BASE="${{ github.event.pull_request.base.sha }}"
|
||||
git fetch --depth=1 origin "$BASE"
|
||||
else
|
||||
BASE="${{ github.event.before }}"
|
||||
if echo "$BASE" | grep -q '^0\{40\}$'; then
|
||||
echo "skip=true" >> "$GITHUB_OUTPUT"
|
||||
echo "New branch push; skipping clang-tidy."
|
||||
exit 0
|
||||
fi
|
||||
fi
|
||||
echo "ref=$BASE" >> "$GITHUB_OUTPUT"
|
||||
echo "skip=false" >> "$GITHUB_OUTPUT"
|
||||
- name: Install clang-tidy
|
||||
if: steps.diff-base.outputs.skip != 'true'
|
||||
run: apt-get update && apt-get install -y clang-tidy-21 && ln -sf /usr/bin/clang-tidy-21 /usr/local/bin/clang-tidy
|
||||
- name: Generate compile_commands.json
|
||||
if: steps.diff-base.outputs.skip != 'true'
|
||||
run: |
|
||||
mkdir build && cd build
|
||||
cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=ON \
|
||||
-DCMAKE_C_COMPILER=clang-21 \
|
||||
-DCMAKE_CXX_COMPILER=clang++-21 ..
|
||||
cd ..
|
||||
ln -sf build/compile_commands.json compile_commands.json
|
||||
- name: Run clang-tidy on changed files
|
||||
id: clang-tidy
|
||||
if: steps.diff-base.outputs.skip != 'true'
|
||||
run: |
|
||||
python3 tools/run_clang_tidy.py \
|
||||
-j 4 \
|
||||
--diff-base ${{ steps.diff-base.outputs.ref }} \
|
||||
--github-annotations \
|
||||
--github-step-summary \
|
||||
--comment-output clang-tidy-comment.md
|
||||
continue-on-error: true
|
||||
- name: Save PR number
|
||||
if: github.event_name == 'pull_request' && always()
|
||||
run: echo "${{ github.event.pull_request.number }}" > pr_number.txt
|
||||
- name: Upload clang-tidy results
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v4.0.0
|
||||
with:
|
||||
name: clang-tidy-result
|
||||
path: |
|
||||
clang-tidy-comment.md
|
||||
pr_number.txt
|
||||
if-no-files-found: ignore
|
||||
- name: Fail if clang-tidy found issues
|
||||
if: steps.clang-tidy.outcome == 'failure'
|
||||
run: exit 1
|
||||
@@ -1,25 +0,0 @@
|
||||
# Claude Code Review — Comment Posting Workflow
|
||||
#
|
||||
# Thin wrapper around .github/workflows/ai-review-comment.yml so the provider
|
||||
# specific workflow name stays stable while the shared implementation lives in
|
||||
# one reusable workflow.
|
||||
|
||||
name: Post Claude Review Comment
|
||||
|
||||
on:
|
||||
workflow_run:
|
||||
workflows: ["Claude Code Review"]
|
||||
types: [completed]
|
||||
|
||||
permissions:
|
||||
pull-requests: write
|
||||
issues: write
|
||||
|
||||
jobs:
|
||||
review-comment:
|
||||
uses: ./.github/workflows/ai-review-comment.yml
|
||||
with:
|
||||
provider: claude
|
||||
result_artifact_name: claude-review-result
|
||||
comment_file: claude-review-comment.md
|
||||
secrets: inherit
|
||||
@@ -1,69 +0,0 @@
|
||||
# Claude Code Review — Analysis Workflow
|
||||
#
|
||||
# Thin wrapper around .github/workflows/ai-review-analysis.yml so provider
|
||||
# specific triggers and workflow_dispatch inputs stay stable while the shared
|
||||
# implementation lives in one reusable workflow.
|
||||
|
||||
name: Claude Code Review
|
||||
|
||||
on:
|
||||
workflow_run:
|
||||
workflows: ["facebook/rocksdb/pr-jobs"]
|
||||
types: [completed]
|
||||
|
||||
# The early pull_request_target path is limited to same-repo PRs by the job
|
||||
# condition below. Fork PRs skip this path and rely on workflow_run instead.
|
||||
pull_request_target:
|
||||
types: [opened, reopened, synchronize]
|
||||
|
||||
issue_comment:
|
||||
types: [created]
|
||||
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
pr_number:
|
||||
description: PR number to review
|
||||
required: true
|
||||
type: number
|
||||
model:
|
||||
description: Claude model to use (defaults to latest Opus)
|
||||
required: false
|
||||
type: choice
|
||||
options:
|
||||
- claude-opus-4-6
|
||||
- claude-sonnet-4-6
|
||||
default: claude-opus-4-6
|
||||
thinking_budget:
|
||||
description: Override MAX_THINKING_TOKENS (blank = auto-classify, capped at 24000)
|
||||
required: false
|
||||
type: string
|
||||
default: ""
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
# The shared analysis workflow polls check state, inspects prior runs, and
|
||||
# reads PR metadata. Keep those permissions read-only in the caller.
|
||||
actions: read
|
||||
checks: read
|
||||
pull-requests: read
|
||||
|
||||
jobs:
|
||||
review:
|
||||
if: >-
|
||||
github.event_name != 'pull_request_target' ||
|
||||
github.event.pull_request.head.repo.full_name == github.repository
|
||||
uses: ./.github/workflows/ai-review-analysis.yml
|
||||
with:
|
||||
provider: claude
|
||||
display_name: Claude
|
||||
review_command: /claude-review
|
||||
query_command: /claude-query
|
||||
result_artifact_name: claude-review-result
|
||||
comment_file: claude-review-comment.md
|
||||
default_model: claude-opus-4-6
|
||||
selected_model: ${{ github.event_name == 'workflow_dispatch' && inputs.model || '' }}
|
||||
classifier_model: claude-sonnet-4-6
|
||||
recovery_model: claude-sonnet-4-6
|
||||
thinking_budget: ${{ github.event_name == 'workflow_dispatch' && inputs.thinking_budget || '' }}
|
||||
dispatch_pr_number: ${{ github.event_name == 'workflow_dispatch' && inputs.pr_number || '' }}
|
||||
secrets: inherit
|
||||
@@ -1,25 +0,0 @@
|
||||
# Codex Code Review — Comment Posting Workflow
|
||||
#
|
||||
# Thin wrapper around .github/workflows/ai-review-comment.yml so the provider
|
||||
# specific workflow name stays stable while the shared implementation lives in
|
||||
# one reusable workflow.
|
||||
|
||||
name: Post Codex Review Comment
|
||||
|
||||
on:
|
||||
workflow_run:
|
||||
workflows: ["Codex Code Review"]
|
||||
types: [completed]
|
||||
|
||||
permissions:
|
||||
pull-requests: write
|
||||
issues: write
|
||||
|
||||
jobs:
|
||||
review-comment:
|
||||
uses: ./.github/workflows/ai-review-comment.yml
|
||||
with:
|
||||
provider: codex
|
||||
result_artifact_name: codex-review-result
|
||||
comment_file: codex-review-comment.md
|
||||
secrets: inherit
|
||||
@@ -1,68 +0,0 @@
|
||||
# Codex Code Review — Analysis Workflow
|
||||
#
|
||||
# Thin wrapper around .github/workflows/ai-review-analysis.yml so provider
|
||||
# specific triggers and workflow_dispatch inputs stay stable while the shared
|
||||
# implementation lives in one reusable workflow.
|
||||
|
||||
name: Codex Code Review
|
||||
|
||||
on:
|
||||
workflow_run:
|
||||
workflows: ["facebook/rocksdb/pr-jobs"]
|
||||
types: [completed]
|
||||
|
||||
# The early pull_request_target path is limited to same-repo PRs by the job
|
||||
# condition below. Fork PRs skip this path and rely on workflow_run instead.
|
||||
pull_request_target:
|
||||
types: [opened, reopened, synchronize]
|
||||
|
||||
issue_comment:
|
||||
types: [created]
|
||||
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
pr_number:
|
||||
description: PR number to review
|
||||
required: true
|
||||
type: number
|
||||
model:
|
||||
description: Codex model to use
|
||||
required: false
|
||||
type: choice
|
||||
options:
|
||||
- gpt-5.5
|
||||
- gpt-5.3-codex
|
||||
- gpt-5.2-codex
|
||||
default: gpt-5.5
|
||||
thinking_budget:
|
||||
description: Override MAX_THINKING_TOKENS (blank = auto-classify)
|
||||
required: false
|
||||
type: string
|
||||
default: ""
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
# The shared analysis workflow polls check state, inspects prior runs, and
|
||||
# reads PR metadata. Keep those permissions read-only in the caller.
|
||||
actions: read
|
||||
checks: read
|
||||
pull-requests: read
|
||||
|
||||
jobs:
|
||||
review:
|
||||
if: >-
|
||||
github.event_name != 'pull_request_target' ||
|
||||
github.event.pull_request.head.repo.full_name == github.repository
|
||||
uses: ./.github/workflows/ai-review-analysis.yml
|
||||
with:
|
||||
provider: codex
|
||||
display_name: Codex
|
||||
review_command: /codex-review
|
||||
query_command: /codex-query
|
||||
result_artifact_name: codex-review-result
|
||||
comment_file: codex-review-comment.md
|
||||
default_model: gpt-5.5
|
||||
selected_model: ${{ github.event_name == 'workflow_dispatch' && inputs.model || '' }}
|
||||
thinking_budget: ${{ github.event_name == 'workflow_dispatch' && inputs.thinking_budget || '' }}
|
||||
dispatch_pr_number: ${{ github.event_name == 'workflow_dispatch' && inputs.pr_number || '' }}
|
||||
secrets: inherit
|
||||
@@ -1,19 +0,0 @@
|
||||
name: facebook/rocksdb/nightly
|
||||
on: workflow_dispatch
|
||||
permissions: {}
|
||||
jobs:
|
||||
# These jobs would be in nightly but are failing or otherwise broken for
|
||||
# some reason.
|
||||
build-linux-arm-test-full:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on:
|
||||
labels: arm64large
|
||||
container:
|
||||
image: ubuntu-2004:202111-02
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- uses: "./.github/actions/install-gflags"
|
||||
- run: make V=1 J=4 -j4 check
|
||||
- uses: "./.github/actions/post-steps"
|
||||
@@ -1,189 +0,0 @@
|
||||
name: facebook/rocksdb/nightly
|
||||
on:
|
||||
schedule:
|
||||
- cron: 0 9 * * *
|
||||
workflow_dispatch:
|
||||
permissions: {}
|
||||
jobs:
|
||||
build-format-compatible:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on:
|
||||
labels: 16-core-ubuntu
|
||||
container:
|
||||
image: ghcr.io/facebook/rocksdb_ubuntu:22.1
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
with:
|
||||
fetch-depth: 0 # Need full repo history
|
||||
fetch-tags: true
|
||||
- uses: "./.github/actions/setup-upstream"
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- name: test
|
||||
run: |-
|
||||
export TEST_TMPDIR=/dev/shm/rocksdb
|
||||
rm -rf /dev/shm/rocksdb
|
||||
mkdir /dev/shm/rocksdb
|
||||
git config --global --add safe.directory /__w/rocksdb/rocksdb
|
||||
tools/check_format_compatible.sh
|
||||
- uses: "./.github/actions/post-steps"
|
||||
build-linux-non-shm:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on:
|
||||
labels: 16-core-ubuntu
|
||||
container:
|
||||
image: ghcr.io/facebook/rocksdb_ubuntu:22.1
|
||||
options: --shm-size=16gb
|
||||
env:
|
||||
TEST_TMPDIR: "/tmp/rocksdb_test_tmp"
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- run: make V=1 -j32 check
|
||||
- uses: "./.github/actions/post-steps"
|
||||
build-linux-clang-21-asan-ubsan-with-folly:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on:
|
||||
labels: 16-core-ubuntu
|
||||
container:
|
||||
image: ghcr.io/facebook/rocksdb_ubuntu:24.1
|
||||
options: --shm-size=16gb
|
||||
env:
|
||||
CC: clang-21
|
||||
CXX: clang++-21
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- uses: "./.github/actions/cache-getdeps-downloads"
|
||||
- uses: "./.github/actions/setup-folly"
|
||||
- name: Build folly and dependencies
|
||||
run: |
|
||||
clean_path=()
|
||||
IFS=: read -ra path_entries <<< "$PATH"
|
||||
for entry in "${path_entries[@]}"; do
|
||||
if [[ "$entry" != "/usr/lib/ccache" ]]; then
|
||||
clean_path+=("$entry")
|
||||
fi
|
||||
done
|
||||
export PATH="$(IFS=:; echo "${clean_path[*]}")"
|
||||
ccache_bin="$(command -v ccache || true)"
|
||||
if [[ -n "$ccache_bin" && -x "$ccache_bin" ]]; then
|
||||
mv "$ccache_bin" "${ccache_bin}.disabled"
|
||||
fi
|
||||
export CC=gcc
|
||||
export CXX=g++
|
||||
export USE_CCACHE=0
|
||||
export CCACHE_DISABLE=1
|
||||
make build_folly
|
||||
shell: bash
|
||||
- run: LIB_MODE=static USE_CLANG=1 USE_FOLLY=1 COMPILE_WITH_UBSAN=1 COMPILE_WITH_ASAN=1 make -j32 check
|
||||
- uses: "./.github/actions/post-steps"
|
||||
build-linux-cmake-with-folly:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on:
|
||||
labels: 16-core-ubuntu
|
||||
container:
|
||||
image: ghcr.io/facebook/rocksdb_ubuntu:22.1
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- uses: "./.github/actions/cache-getdeps-downloads"
|
||||
- uses: "./.github/actions/setup-folly"
|
||||
- uses: "./.github/actions/cache-folly"
|
||||
id: cache-folly
|
||||
- uses: "./.github/actions/build-folly"
|
||||
with:
|
||||
cache-hit: ${{ steps.cache-folly.outputs.cache-hit }}
|
||||
- run: "(mkdir build && cd build && cmake -DUSE_FOLLY=1 -DWITH_GFLAGS=1 -DROCKSDB_BUILD_SHARED=0 .. && make VERBOSE=1 -j20 && ctest -j20)"
|
||||
- uses: "./.github/actions/post-steps"
|
||||
build-linux-release-with-folly:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on:
|
||||
labels: 16-core-ubuntu
|
||||
container:
|
||||
image: ghcr.io/facebook/rocksdb_ubuntu:22.1
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- uses: "./.github/actions/cache-getdeps-downloads"
|
||||
- uses: "./.github/actions/setup-folly"
|
||||
- run: "DEBUG_LEVEL=0 make -j20 build_folly"
|
||||
- run: "USE_FOLLY=1 LIB_MODE=static DEBUG_LEVEL=0 V=1 make -j20 release"
|
||||
- run: "(mkdir build && cd build && cmake -DUSE_FOLLY=1 -DWITH_GFLAGS=1 -DROCKSDB_BUILD_SHARED=0 -DCMAKE_BUILD_TYPE=Release .. && make VERBOSE=1 -j20 && ctest -j20)"
|
||||
- uses: "./.github/actions/post-steps"
|
||||
build-windows-msvc-avx2:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on: windows-8-core
|
||||
env:
|
||||
CMAKE_PORTABLE: AVX2
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/windows-build-steps"
|
||||
build-linux-arm-test-full:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on:
|
||||
labels: 4-core-ubuntu-arm
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- run: sudo apt-get update && sudo apt-get install -y build-essential libgflags-dev
|
||||
- run: make V=1 J=4 -j4 check
|
||||
- uses: "./.github/actions/post-steps"
|
||||
build-linux-arm-crashtest:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on:
|
||||
labels: 4-core-ubuntu-arm
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- run: sudo apt-get update && sudo apt-get install -y build-essential libgflags-dev libsnappy-dev zlib1g-dev libbz2-dev liblz4-dev libzstd-dev
|
||||
- run: sudo mount -o remount,size=16G /dev/shm
|
||||
- run: sudo dd bs=1048576 count=4096 if=/dev/zero of=/swapfile && sudo chmod 600 /swapfile && sudo mkswap /swapfile && sudo swapon /swapfile
|
||||
- run: ulimit -S -n `ulimit -H -n` && make V=1 -j8 CRASH_TEST_EXT_ARGS='--duration=1800 --max_key=2500000' blackbox_crash_test_with_atomic_flush
|
||||
- run: rm -rf /dev/shm/rocksdb.*
|
||||
- run: ulimit -S -n `ulimit -H -n` && make V=1 -j8 CRASH_TEST_EXT_ARGS='--duration=1800 --max_key=2500000' blackbox_crash_test_with_multiops_wc_txn
|
||||
- uses: "./.github/actions/post-steps"
|
||||
build-examples:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on:
|
||||
labels: 4-core-ubuntu
|
||||
container:
|
||||
image: ghcr.io/facebook/rocksdb_ubuntu:22.1
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- name: Build examples
|
||||
run: make V=1 -j4 static_lib && cd examples && make V=1 -j4
|
||||
- uses: "./.github/actions/post-steps"
|
||||
build-fuzzers:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on:
|
||||
labels: 4-core-ubuntu
|
||||
container:
|
||||
image: ghcr.io/facebook/rocksdb_ubuntu:24.1
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- name: Build rocksdb lib
|
||||
run: CC=clang-21 CXX=clang++-21 USE_CLANG=1 make -j4 static_lib
|
||||
- name: Build fuzzers
|
||||
run: cd fuzz && make sst_file_writer_fuzzer db_fuzzer db_map_fuzzer
|
||||
- uses: "./.github/actions/post-steps"
|
||||
build-linux-cmake-with-folly-lite:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on:
|
||||
labels: 16-core-ubuntu
|
||||
container:
|
||||
image: ghcr.io/facebook/rocksdb_ubuntu:22.1
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- uses: "./.github/actions/cache-getdeps-downloads"
|
||||
- uses: "./.github/actions/setup-folly"
|
||||
- run: "(mkdir build && cd build && cmake -DUSE_FOLLY_LITE=1 -DWITH_GFLAGS=1 -DCMAKE_CXX_FLAGS=-DGLOG_USE_GLOG_EXPORT .. && make VERBOSE=1 -j20 && ctest -j20)"
|
||||
- uses: "./.github/actions/post-steps"
|
||||
@@ -1,47 +0,0 @@
|
||||
name: facebook/rocksdb/pr-jobs-candidate
|
||||
on: workflow_dispatch
|
||||
permissions: {}
|
||||
jobs:
|
||||
# These jobs would be in pr-jobs but are failing or otherwise broken for
|
||||
# some reason.
|
||||
# =========================== ARM Jobs ============================ #
|
||||
build-linux-arm:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on:
|
||||
labels: arm64large # GitHub hosted ARM runners do not yet exist
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- uses: "./.github/actions/install-gflags"
|
||||
- run: ROCKSDBTESTS_PLATFORM_DEPENDENT=only make V=1 J=4 -j4 all_but_some_tests check_some
|
||||
- uses: "./.github/actions/post-steps"
|
||||
build-linux-arm-cmake-no_test_run:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on:
|
||||
labels: arm64large # GitHub hosted ARM runners do not yet exist
|
||||
env:
|
||||
JAVA_HOME: "/usr/lib/jvm/java-8-openjdk-arm64"
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- uses: "./.github/actions/install-gflags"
|
||||
- name: Set Java Environment
|
||||
run: |-
|
||||
echo "JAVA_HOME=${JAVA_HOME}"
|
||||
echo 'export PATH=$JAVA_HOME/bin:$PATH' >> $BASH_ENV
|
||||
which java && java -version
|
||||
which javac && javac -version
|
||||
- name: Build with cmake
|
||||
run: |-
|
||||
mkdir build
|
||||
cd build
|
||||
cmake -DCMAKE_BUILD_TYPE=Release -DWITH_TESTS=0 -DWITH_GFLAGS=1 -DWITH_BENCHMARK_TOOLS=0 -DWITH_TOOLS=0 -DWITH_CORE_TOOLS=1 ..
|
||||
make -j4
|
||||
- name: Build Java with cmake
|
||||
run: |-
|
||||
rm -rf build
|
||||
mkdir build
|
||||
cd build
|
||||
cmake -DJNI=1 -DCMAKE_BUILD_TYPE=Release -DWITH_GFLAGS=1 ..
|
||||
make -j4 rocksdb rocksdbjni
|
||||
- uses: "./.github/actions/post-steps"
|
||||
@@ -1,741 +0,0 @@
|
||||
name: facebook/rocksdb/pr-jobs
|
||||
on: [push, pull_request]
|
||||
permissions: {}
|
||||
env:
|
||||
# Set to a job name to run only that job (on any repo), or leave empty for
|
||||
# normal behavior (all jobs on facebook repo only).
|
||||
ONLY_JOB: ''
|
||||
jobs:
|
||||
config:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
only_job: ${{ steps.set.outputs.only_job }}
|
||||
steps:
|
||||
- id: set
|
||||
run: echo "only_job=$ONLY_JOB" >> "$GITHUB_OUTPUT"
|
||||
# NOTE: multiple workflows would be recommended, but the current GHA UI in
|
||||
# PRs doesn't make it clear when there's an overall error with a workflow,
|
||||
# making it easy to overlook something broken. Grouping everything into one
|
||||
# workflow minimizes the problem because it will be suspicious if there are
|
||||
# no GHA results.
|
||||
#
|
||||
# The if: ${{ github.repository_owner == 'facebook' }} lines prevent the
|
||||
# jobs from attempting to run on repo forks, because of a few problems:
|
||||
# * runs-on labels are repository (owner) specific, so the job might wait
|
||||
# for days waiting for a runner that simply isn't available.
|
||||
# * Pushes to branches on forks for pull requests (the normal process) would
|
||||
# run the workflow jobs twice: once in the pull-from fork and once for the PR
|
||||
# destination repo. This is wasteful and dumb.
|
||||
# * It is not known how to avoid copy-pasting the line to each job,
|
||||
# increasing the risk of misconfiguration, especially on forks that might
|
||||
# want to run with this GHA setup.
|
||||
#
|
||||
# SELECTIVE JOB EXECUTION: Set the ONLY_JOB env var at the top of this file
|
||||
# to a job name (e.g. "build-linux-clang-tidy") to run only that job,
|
||||
# bypassing the repository owner check. Leave it empty for normal behavior.
|
||||
#
|
||||
# DEBUGGING WITH SSH: Temporarily add this as a job step, either before the
|
||||
# step of interest without the "if:" line or after the failing step with the
|
||||
# "if:" line. Then use ssh command printed in CI output.
|
||||
# - name: Setup tmate session # TEMPORARY!
|
||||
# if: ${{ failure() }}
|
||||
# uses: mxschmitt/action-tmate@v3
|
||||
# with:
|
||||
# limit-access-to-actor: true
|
||||
|
||||
# ======================== Fast Initial Checks ====================== #
|
||||
check-format-and-targets:
|
||||
if: needs.config.outputs.only_job == 'check-format-and-targets' || (needs.config.outputs.only_job == '' && github.repository_owner == 'facebook')
|
||||
needs: config
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
with:
|
||||
fetch-depth: 0 # Need full checkout to determine merge base
|
||||
fetch-tags: true
|
||||
- uses: "./.github/actions/setup-upstream"
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v5
|
||||
- name: Install Dependencies
|
||||
run: python -m pip install --upgrade pip
|
||||
- name: Install argparse
|
||||
run: pip install argparse
|
||||
- name: Install clang-format
|
||||
run: |
|
||||
pip install https://files.pythonhosted.org/packages/fb/ac/3c04772acc0257f5730e83adb542b2603c1a62d1315010ab593a980af404/clang_format-21.1.2-py2.py3-none-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
|
||||
clang-format --version
|
||||
- name: Download clang-format-diff.py
|
||||
run: wget https://rocksdb-deps.s3.us-west-2.amazonaws.com/llvm/llvm-project/release/12.x/clang/tools/clang-format/clang-format-diff.py
|
||||
- name: Check format
|
||||
run: VERBOSE_CHECK=1 make check-format
|
||||
- name: Compare buckify output
|
||||
run: make check-buck-targets
|
||||
- name: Simple source code checks
|
||||
run: make check-sources
|
||||
- name: Validate GitHub Actions YAML
|
||||
run: make check-workflow-yaml
|
||||
- name: Sanity check check_format_compatible.sh
|
||||
run: |-
|
||||
export TEST_TMPDIR=/dev/shm/rocksdb
|
||||
rm -rf /dev/shm/rocksdb
|
||||
mkdir /dev/shm/rocksdb
|
||||
git reset --hard
|
||||
git config --global --add safe.directory /__w/rocksdb/rocksdb
|
||||
SANITY_CHECK=1 LONG_TEST=1 tools/check_format_compatible.sh
|
||||
# ========================= Linux With Tests ======================== #
|
||||
build-linux:
|
||||
if: needs.config.outputs.only_job == 'build-linux' || (needs.config.outputs.only_job == '' && github.repository_owner == 'facebook')
|
||||
needs: config
|
||||
runs-on:
|
||||
labels: 16-core-ubuntu
|
||||
container:
|
||||
image: ghcr.io/facebook/rocksdb_ubuntu:22.1
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- uses: "./.github/actions/setup-ccache"
|
||||
with:
|
||||
cache-key-prefix: build-linux
|
||||
- run: make V=1 J=32 -j32 check
|
||||
- uses: "./.github/actions/teardown-ccache"
|
||||
if: always()
|
||||
- uses: "./.github/actions/post-steps"
|
||||
build-linux-cmake-mingw:
|
||||
if: needs.config.outputs.only_job == 'build-linux-cmake-mingw' || (needs.config.outputs.only_job == '' && github.repository_owner == 'facebook')
|
||||
needs: config
|
||||
runs-on:
|
||||
labels: 4-core-ubuntu
|
||||
container:
|
||||
image: ghcr.io/facebook/rocksdb_ubuntu:24.1
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- uses: "./.github/actions/setup-ccache"
|
||||
with:
|
||||
cache-key-prefix: cmake-mingw
|
||||
- run: update-alternatives --set x86_64-w64-mingw32-g++ /usr/bin/x86_64-w64-mingw32-g++-posix
|
||||
- name: Build cmake-mingw
|
||||
run: |-
|
||||
export PATH=$JAVA_HOME/bin:$PATH
|
||||
echo "JAVA_HOME=${JAVA_HOME}"
|
||||
which java && java -version
|
||||
which javac && javac -version
|
||||
mkdir build && cd build && cmake -DJNI=1 -DWITH_GFLAGS=OFF .. -DCMAKE_C_COMPILER=x86_64-w64-mingw32-gcc -DCMAKE_CXX_COMPILER=x86_64-w64-mingw32-g++ -DCMAKE_SYSTEM_NAME=Windows && make -j4 rocksdb rocksdbjni
|
||||
- uses: "./.github/actions/teardown-ccache"
|
||||
if: always()
|
||||
- uses: "./.github/actions/post-steps"
|
||||
build-linux-make-with-folly:
|
||||
if: needs.config.outputs.only_job == 'build-linux-make-with-folly' || (needs.config.outputs.only_job == '' && github.repository_owner == 'facebook')
|
||||
needs: config
|
||||
runs-on:
|
||||
labels: 32-core-ubuntu
|
||||
container:
|
||||
image: ghcr.io/facebook/rocksdb_ubuntu:22.1
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- uses: "./.github/actions/cache-getdeps-downloads"
|
||||
- uses: "./.github/actions/setup-folly"
|
||||
- uses: "./.github/actions/setup-ccache"
|
||||
with:
|
||||
cache-key-prefix: make-with-folly
|
||||
- uses: "./.github/actions/cache-folly"
|
||||
id: cache-folly
|
||||
- uses: "./.github/actions/build-folly"
|
||||
with:
|
||||
cache-hit: ${{ steps.cache-folly.outputs.cache-hit }}
|
||||
- run: USE_FOLLY=1 LIB_MODE=static V=1 make -j64 check
|
||||
- uses: "./.github/actions/teardown-ccache"
|
||||
if: always()
|
||||
- uses: "./.github/actions/post-steps"
|
||||
build-linux-make-with-folly-lite-no-test:
|
||||
if: needs.config.outputs.only_job == 'build-linux-make-with-folly-lite-no-test' || (needs.config.outputs.only_job == '' && github.repository_owner == 'facebook')
|
||||
needs: config
|
||||
runs-on:
|
||||
labels: 16-core-ubuntu
|
||||
container:
|
||||
image: ghcr.io/facebook/rocksdb_ubuntu:22.1
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- uses: "./.github/actions/cache-getdeps-downloads"
|
||||
- uses: "./.github/actions/setup-folly"
|
||||
- uses: "./.github/actions/setup-ccache"
|
||||
with:
|
||||
cache-key-prefix: make-folly-lite
|
||||
- run: USE_FOLLY_LITE=1 EXTRA_CXXFLAGS=-DGLOG_USE_GLOG_EXPORT V=1 make -j32 all
|
||||
- uses: "./.github/actions/teardown-ccache"
|
||||
if: always()
|
||||
- uses: "./.github/actions/post-steps"
|
||||
build-linux-cmake-with-folly-coroutines:
|
||||
if: needs.config.outputs.only_job == 'build-linux-cmake-with-folly-coroutines' || (needs.config.outputs.only_job == '' && github.repository_owner == 'facebook')
|
||||
needs: config
|
||||
runs-on:
|
||||
labels: 32-core-ubuntu
|
||||
container:
|
||||
image: ghcr.io/facebook/rocksdb_ubuntu:22.1
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- uses: "./.github/actions/cache-getdeps-downloads"
|
||||
- uses: "./.github/actions/setup-folly"
|
||||
- uses: "./.github/actions/setup-ccache"
|
||||
with:
|
||||
cache-key-prefix: cmake-folly-coroutines
|
||||
- uses: "./.github/actions/cache-folly"
|
||||
id: cache-folly
|
||||
- uses: "./.github/actions/build-folly"
|
||||
with:
|
||||
cache-hit: ${{ steps.cache-folly.outputs.cache-hit }}
|
||||
- run: "(mkdir build && cd build && cmake -DUSE_COROUTINES=1 -DWITH_GFLAGS=1 -DROCKSDB_BUILD_SHARED=0 -DPORTABLE=ON .. && make VERBOSE=1 -j64 && ctest -j64)"
|
||||
- uses: "./.github/actions/teardown-ccache"
|
||||
if: always()
|
||||
- uses: "./.github/actions/post-steps"
|
||||
build-linux-cmake-with-benchmark-no-thread-status:
|
||||
if: needs.config.outputs.only_job == 'build-linux-cmake-with-benchmark-no-thread-status' || (needs.config.outputs.only_job == '' && github.repository_owner == 'facebook')
|
||||
needs: config
|
||||
runs-on:
|
||||
labels: 16-core-ubuntu
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
shard: [0, 1, 2, 3]
|
||||
container:
|
||||
image: ghcr.io/facebook/rocksdb_ubuntu:22.1
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- uses: "./.github/actions/setup-ccache"
|
||||
with:
|
||||
cache-key-prefix: cmake-benchmark
|
||||
- name: Build
|
||||
run: mkdir build && cd build && cmake -DWITH_GFLAGS=1 -DWITH_BENCHMARK=1 -DPORTABLE=ON -DCMAKE_CXX_FLAGS=-DNROCKSDB_THREAD_STATUS .. && make VERBOSE=1 -j20
|
||||
- name: Test shard ${{ matrix.shard }} of 4
|
||||
run: cd build && ctest -j20 -I ${{ matrix.shard }},,4
|
||||
- uses: "./.github/actions/teardown-ccache"
|
||||
if: always()
|
||||
- uses: "./.github/actions/post-steps"
|
||||
with:
|
||||
artifact-prefix: "${{ github.job }}-shard${{ matrix.shard }}"
|
||||
build-linux-encrypted_env-no_compression:
|
||||
if: needs.config.outputs.only_job == 'build-linux-encrypted_env-no_compression' || (needs.config.outputs.only_job == '' && github.repository_owner == 'facebook')
|
||||
needs: config
|
||||
runs-on:
|
||||
labels: 16-core-ubuntu
|
||||
container:
|
||||
image: ghcr.io/facebook/rocksdb_ubuntu:22.1
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- uses: "./.github/actions/setup-ccache"
|
||||
with:
|
||||
cache-key-prefix: encrypted-env-no-compression
|
||||
- run: ENCRYPTED_ENV=1 ROCKSDB_DISABLE_SNAPPY=1 ROCKSDB_DISABLE_ZLIB=1 ROCKSDB_DISABLE_BZIP=1 ROCKSDB_DISABLE_LZ4=1 ROCKSDB_DISABLE_ZSTD=1 make V=1 J=32 -j32 check
|
||||
- run: "./sst_dump --help | grep -E -q 'Supported built-in compression types: kNoCompression$' # Verify no compiled in compression\n"
|
||||
- uses: "./.github/actions/teardown-ccache"
|
||||
if: always()
|
||||
- uses: "./.github/actions/post-steps"
|
||||
# ======================== Linux No Test Runs ======================= #
|
||||
build-linux-release:
|
||||
if: needs.config.outputs.only_job == 'build-linux-release' || (needs.config.outputs.only_job == '' && github.repository_owner == 'facebook')
|
||||
needs: config
|
||||
runs-on:
|
||||
labels: 16-core-ubuntu
|
||||
container:
|
||||
image: ghcr.io/facebook/rocksdb_ubuntu:22.1
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/setup-ccache"
|
||||
with:
|
||||
cache-key-prefix: release
|
||||
- run: make V=1 -j32 LIB_MODE=shared release
|
||||
- run: ls librocksdb.so
|
||||
- run: "./trace_analyzer --version" # A tool dependent on gflags that can run in release build
|
||||
- run: make clean
|
||||
- run: USE_RTTI=1 make V=1 -j32 release
|
||||
- run: ls librocksdb.a
|
||||
- run: "./trace_analyzer --version"
|
||||
- run: make clean
|
||||
- run: apt-get remove -y libgflags-dev
|
||||
- run: make V=1 -j32 LIB_MODE=shared release
|
||||
- run: ls librocksdb.so
|
||||
- run: if ./trace_analyzer --version; then false; else true; fi
|
||||
- run: make clean
|
||||
- run: USE_RTTI=1 make V=1 -j32 release
|
||||
- run: ls librocksdb.a
|
||||
- run: if ./trace_analyzer --version; then false; else true; fi
|
||||
- uses: "./.github/actions/teardown-ccache"
|
||||
if: always()
|
||||
- uses: "./.github/actions/post-steps"
|
||||
build-linux-clang-13-no_test_run:
|
||||
if: needs.config.outputs.only_job == 'build-linux-clang-13-no_test_run' || (needs.config.outputs.only_job == '' && github.repository_owner == 'facebook')
|
||||
needs: config
|
||||
runs-on:
|
||||
labels: 8-core-ubuntu
|
||||
container:
|
||||
image: ghcr.io/facebook/rocksdb_ubuntu:22.1
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- uses: "./.github/actions/setup-ccache"
|
||||
with:
|
||||
cache-key-prefix: clang-13
|
||||
# FIXME: get back to "all microbench" targets
|
||||
- run: CC=clang-13 CXX=clang++-13 USE_CLANG=1 EXTRA_CXXFLAGS=-stdlib=libc++ EXTRA_LDFLAGS=-stdlib=libc++ make -j32 shared_lib
|
||||
- run: make clean
|
||||
# FIXME: get back to "release" target
|
||||
- run: CC=clang-13 CXX=clang++-13 USE_CLANG=1 EXTRA_CXXFLAGS=-stdlib=libc++ EXTRA_LDFLAGS=-stdlib=libc++ DEBUG_LEVEL=0 make -j32 shared_lib
|
||||
- uses: "./.github/actions/teardown-ccache"
|
||||
if: always()
|
||||
- uses: "./.github/actions/post-steps"
|
||||
build-linux-clang-21-no_test_run:
|
||||
if: needs.config.outputs.only_job == 'build-linux-clang-21-no_test_run' || (needs.config.outputs.only_job == '' && github.repository_owner == 'facebook')
|
||||
needs: config
|
||||
runs-on:
|
||||
labels: 16-core-ubuntu
|
||||
container:
|
||||
image: ghcr.io/facebook/rocksdb_ubuntu:24.1
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- uses: "./.github/actions/setup-ccache"
|
||||
with:
|
||||
cache-key-prefix: clang-21
|
||||
- run: CC=clang-21 CXX=clang++-21 USE_CLANG=1 make -j32 all microbench
|
||||
- run: make clean
|
||||
- run: CC=clang-21 CXX=clang++-21 USE_CLANG=1 DEBUG_LEVEL=0 make -j32 release
|
||||
- name: Install clang-format-21 for C API gen checks
|
||||
run: apt-get update && apt-get install -y clang-format-21
|
||||
- name: Check C API codegen (link-complete, backward-compatible, up to date)
|
||||
# Run the make target in STRICT mode so this core CI job HARD-FAILS rather
|
||||
# than silently skipping if a prerequisite (clang++, clang-format, or the
|
||||
# compat baseline ref) is ever missing. See `make check-c-api-gen`.
|
||||
run: |
|
||||
# The container checkout is owned by a different user, so git refuses to
|
||||
# operate on it ("dubious ownership") -- mark it safe for the `git fetch`
|
||||
# and the `git show` the compat checker runs.
|
||||
git config --global --add safe.directory "$GITHUB_WORKSPACE"
|
||||
git config --global --add safe.directory '*'
|
||||
git fetch --no-tags --depth=1 origin main
|
||||
CXX=clang++-21 make check-c-api-gen \
|
||||
CHECK_C_API_GEN_STRICT=1 \
|
||||
API_COMPAT_REF=FETCH_HEAD \
|
||||
CLANG_FORMAT_BINARY=clang-format-21
|
||||
- uses: "./.github/actions/teardown-ccache"
|
||||
if: always()
|
||||
- uses: "./.github/actions/post-steps"
|
||||
build-linux-gcc-14-no_test_run:
|
||||
if: needs.config.outputs.only_job == 'build-linux-gcc-14-no_test_run' || (needs.config.outputs.only_job == '' && github.repository_owner == 'facebook')
|
||||
needs: config
|
||||
runs-on:
|
||||
labels: 16-core-ubuntu
|
||||
container:
|
||||
image: ghcr.io/facebook/rocksdb_ubuntu:24.1
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- uses: "./.github/actions/setup-ccache"
|
||||
with:
|
||||
cache-key-prefix: gcc-14
|
||||
- run: CC=gcc-14 CXX=g++-14 V=1 make -j32 all microbench
|
||||
- uses: "./.github/actions/teardown-ccache"
|
||||
if: always()
|
||||
- uses: "./.github/actions/post-steps"
|
||||
|
||||
# ======================== Linux Other Checks ======================= #
|
||||
|
||||
build-linux-unity-and-headers:
|
||||
if: needs.config.outputs.only_job == 'build-linux-unity-and-headers' || (needs.config.outputs.only_job == '' && github.repository_owner == 'facebook')
|
||||
needs: config
|
||||
runs-on:
|
||||
labels: 4-core-ubuntu
|
||||
container:
|
||||
image: gcc:latest
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- run: apt-get update -y && apt-get install -y libgflags-dev
|
||||
- uses: "./.github/actions/setup-ccache"
|
||||
with:
|
||||
cache-key-prefix: unity-headers
|
||||
- name: Unity build
|
||||
run: make V=1 -j8 unity_test
|
||||
- run: make V=1 -j8 -k check-headers
|
||||
- uses: "./.github/actions/teardown-ccache"
|
||||
if: always()
|
||||
- uses: "./.github/actions/post-steps"
|
||||
build-linux-mini-crashtest:
|
||||
if: needs.config.outputs.only_job == 'build-linux-mini-crashtest' || (needs.config.outputs.only_job == '' && github.repository_owner == 'facebook')
|
||||
needs: config
|
||||
runs-on:
|
||||
labels: 4-core-ubuntu
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- crash_test_target: blackbox_crash_test_with_atomic_flush
|
||||
crash_duration: 480
|
||||
- crash_test_target: blackbox_crash_test
|
||||
crash_duration: 240
|
||||
container:
|
||||
image: ghcr.io/facebook/rocksdb_ubuntu:22.1
|
||||
options: --shm-size=16gb --ulimit nofile=1048576:1048576
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- uses: "./.github/actions/setup-ccache"
|
||||
with:
|
||||
cache-key-prefix: mini-crashtest
|
||||
- run: |
|
||||
ulimit -S -n $(ulimit -Hn) # Raise open files soft limit to hard limit
|
||||
echo "Updated ulimit -Hn: $(ulimit -Hn) -Sn: $(ulimit -Sn)"
|
||||
make V=1 -j8 CRASH_TEST_EXT_ARGS='--duration=${{ matrix.crash_duration }} --max_key=2500000' ${{ matrix.crash_test_target }}
|
||||
- uses: "./.github/actions/teardown-ccache"
|
||||
if: always()
|
||||
- uses: "./.github/actions/post-steps"
|
||||
with:
|
||||
artifact-prefix: "${{ github.job }}-${{ matrix.crash_test_target }}"
|
||||
# ======================= Linux with Sanitizers ===================== #
|
||||
build-linux-clang21-asan-ubsan:
|
||||
if: needs.config.outputs.only_job == 'build-linux-clang21-asan-ubsan' || (needs.config.outputs.only_job == '' && github.repository_owner == 'facebook')
|
||||
needs: config
|
||||
runs-on:
|
||||
labels: 32-core-ubuntu
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
shard: [0, 1, 2]
|
||||
container:
|
||||
image: ghcr.io/facebook/rocksdb_ubuntu:24.1
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- uses: "./.github/actions/setup-ccache"
|
||||
with:
|
||||
cache-key-prefix: clang21-asan-ubsan
|
||||
- run: COMPILE_WITH_ASAN=1 COMPILE_WITH_UBSAN=1 CC=clang-21 CXX=clang++-21 ROCKSDB_DISABLE_ALIGNED_NEW=1 USE_CLANG=1 make V=1 -j40 check
|
||||
env:
|
||||
CI_SHARD_INDEX: ${{ matrix.shard }}
|
||||
CI_TOTAL_SHARDS: 3
|
||||
- uses: "./.github/actions/teardown-ccache"
|
||||
if: always()
|
||||
- uses: "./.github/actions/post-steps"
|
||||
with:
|
||||
artifact-prefix: "${{ github.job }}-shard${{ matrix.shard }}"
|
||||
build-linux-clang21-mini-tsan:
|
||||
if: needs.config.outputs.only_job == 'build-linux-clang21-mini-tsan' || (needs.config.outputs.only_job == '' && github.repository_owner == 'facebook')
|
||||
needs: config
|
||||
runs-on:
|
||||
labels: 32-core-ubuntu
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
shard: [0, 1, 2]
|
||||
container:
|
||||
image: ghcr.io/facebook/rocksdb_ubuntu:24.1
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- uses: "./.github/actions/setup-ccache"
|
||||
with:
|
||||
cache-key-prefix: clang21-tsan
|
||||
- run: COMPILE_WITH_TSAN=1 CC=clang-21 CXX=clang++-21 ROCKSDB_DISABLE_ALIGNED_NEW=1 USE_CLANG=1 make V=1 -j32 check
|
||||
env:
|
||||
CI_SHARD_INDEX: ${{ matrix.shard }}
|
||||
CI_TOTAL_SHARDS: 3
|
||||
- uses: "./.github/actions/teardown-ccache"
|
||||
if: always()
|
||||
- uses: "./.github/actions/post-steps"
|
||||
with:
|
||||
artifact-prefix: "${{ github.job }}-shard${{ matrix.shard }}"
|
||||
build-linux-static_lib-alt_namespace-status_checked:
|
||||
if: needs.config.outputs.only_job == 'build-linux-static_lib-alt_namespace-status_checked' || (needs.config.outputs.only_job == '' && github.repository_owner == 'facebook')
|
||||
needs: config
|
||||
runs-on:
|
||||
labels: 16-core-ubuntu
|
||||
container:
|
||||
image: ghcr.io/facebook/rocksdb_ubuntu:22.1
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- uses: "./.github/actions/setup-ccache"
|
||||
with:
|
||||
cache-key-prefix: static-alt-namespace
|
||||
- run: ASSERT_STATUS_CHECKED=1 TEST_UINT128_COMPAT=1 ROCKSDB_MODIFY_NPHASH=1 LIB_MODE=static OPT="-DROCKSDB_USE_STD_SEMAPHORES -DROCKSDB_NAMESPACE=alternative_rocksdb_ns" make V=1 -j24 check
|
||||
- uses: "./.github/actions/teardown-ccache"
|
||||
if: always()
|
||||
- uses: "./.github/actions/post-steps"
|
||||
# ========================= MacOS build only ======================== #
|
||||
build-macos:
|
||||
if: needs.config.outputs.only_job == 'build-macos' || (needs.config.outputs.only_job == '' && github.repository_owner == 'facebook')
|
||||
needs: config
|
||||
runs-on: macos-15-xlarge
|
||||
env:
|
||||
ROCKSDB_DISABLE_JEMALLOC: 1
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: maxim-lobanov/setup-xcode@v1.6.0
|
||||
with:
|
||||
xcode-version: 16.4.0
|
||||
- uses: "./.github/actions/setup-ccache"
|
||||
with:
|
||||
cache-key-prefix: macos
|
||||
- uses: "./.github/actions/increase-max-open-files-on-macos"
|
||||
- uses: "./.github/actions/install-gflags-on-macos"
|
||||
- uses: "./.github/actions/pre-steps-macos"
|
||||
- name: Build
|
||||
run: ulimit -S -n `ulimit -H -n` && make V=1 J=16 -j8 all
|
||||
- uses: "./.github/actions/teardown-ccache"
|
||||
if: always()
|
||||
- uses: "./.github/actions/post-steps"
|
||||
# ========================= MacOS with Tests ======================== #
|
||||
build-macos-cmake:
|
||||
if: needs.config.outputs.only_job == 'build-macos-cmake' || (needs.config.outputs.only_job == '' && github.repository_owner == 'facebook')
|
||||
needs: config
|
||||
runs-on: macos-15-xlarge
|
||||
strategy:
|
||||
matrix:
|
||||
run_sharded_tests: [0, 1, 2, 3]
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: maxim-lobanov/setup-xcode@v1.6.0
|
||||
with:
|
||||
xcode-version: 16.4.0
|
||||
- uses: "./.github/actions/setup-ccache"
|
||||
with:
|
||||
cache-key-prefix: macos-cmake
|
||||
- uses: "./.github/actions/increase-max-open-files-on-macos"
|
||||
- uses: "./.github/actions/install-gflags-on-macos"
|
||||
- uses: "./.github/actions/pre-steps-macos"
|
||||
- name: cmake generate project file
|
||||
run: ulimit -S -n `ulimit -H -n` && mkdir build && cd build && cmake -DWITH_GFLAGS=1 ..
|
||||
- name: Build tests
|
||||
run: cd build && make VERBOSE=1 -j8
|
||||
- name: Run shard 0 out of 4 test shards
|
||||
run: ulimit -S -n `ulimit -H -n` && cd build && ctest -j8 -I 0,,4
|
||||
if: ${{ matrix.run_sharded_tests == 0 }}
|
||||
- name: Run shard 1 out of 4 test shards
|
||||
run: ulimit -S -n `ulimit -H -n` && cd build && ctest -j8 -I 1,,4
|
||||
if: ${{ matrix.run_sharded_tests == 1 }}
|
||||
- name: Run shard 2 out of 4 test shards
|
||||
run: ulimit -S -n `ulimit -H -n` && cd build && ctest -j8 -I 2,,4
|
||||
if: ${{ matrix.run_sharded_tests == 2 }}
|
||||
- name: Run shard 3 out of 4 test shards
|
||||
run: ulimit -S -n `ulimit -H -n` && cd build && ctest -j8 -I 3,,4
|
||||
if: ${{ matrix.run_sharded_tests == 3 }}
|
||||
- uses: "./.github/actions/teardown-ccache"
|
||||
if: always()
|
||||
- uses: "./.github/actions/post-steps"
|
||||
# ======================== Windows with Tests ======================= #
|
||||
# NOTE: some windows jobs are in "nightly" to save resources
|
||||
build-windows-msvc:
|
||||
if: needs.config.outputs.only_job == 'build-windows-msvc' || (needs.config.outputs.only_job == '' && github.repository_owner == 'facebook')
|
||||
needs: config
|
||||
runs-on: windows-8-core
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- test_shard: db_test
|
||||
suite_run: db_test
|
||||
run_java: "false"
|
||||
- test_shard: other
|
||||
suite_run: arena_test,db_basic_test,db_etc2_test,db_merge_operand_test,bloom_test,c_test,coding_test,crc32c_test,dynamic_bloom_test,env_basic_test,env_test,hash_test,random_test
|
||||
run_java: "false"
|
||||
- test_shard: java
|
||||
suite_run: ""
|
||||
run_java: "true"
|
||||
env:
|
||||
CMAKE_PORTABLE: 1
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/windows-build-steps"
|
||||
with:
|
||||
suite-run: ${{ matrix.suite_run }}
|
||||
run-java: ${{ matrix.run_java }}
|
||||
# ============================ Java Jobs ============================ #
|
||||
build-linux-java:
|
||||
if: needs.config.outputs.only_job == 'build-linux-java' || (needs.config.outputs.only_job == '' && github.repository_owner == 'facebook')
|
||||
needs: config
|
||||
runs-on:
|
||||
labels: 4-core-ubuntu
|
||||
container:
|
||||
image: ghcr.io/facebook/rocksdb_ubuntu:22.1
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- uses: "./.github/actions/setup-ccache"
|
||||
with:
|
||||
cache-key-prefix: java
|
||||
- name: Set Java Environment
|
||||
run: |-
|
||||
echo "JAVA_HOME=${JAVA_HOME}"
|
||||
which java && java -version
|
||||
which javac && javac -version
|
||||
- name: Test RocksDBJava
|
||||
run: make V=1 J=8 -j8 jtest
|
||||
- uses: "./.github/actions/teardown-ccache"
|
||||
if: always()
|
||||
build-linux-java-static:
|
||||
if: needs.config.outputs.only_job == 'build-linux-java-static' || (needs.config.outputs.only_job == '' && github.repository_owner == 'facebook')
|
||||
needs: config
|
||||
runs-on:
|
||||
labels: 4-core-ubuntu
|
||||
container:
|
||||
image: ghcr.io/facebook/rocksdb_ubuntu:22.1
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- uses: "./.github/actions/setup-ccache"
|
||||
with:
|
||||
cache-key-prefix: java-static
|
||||
- name: Set Java Environment
|
||||
run: |-
|
||||
echo "JAVA_HOME=${JAVA_HOME}"
|
||||
which java && java -version
|
||||
which javac && javac -version
|
||||
- name: Build RocksDBJava Static Library
|
||||
run: make V=1 J=8 -j8 rocksdbjavastatic
|
||||
- uses: "./.github/actions/teardown-ccache"
|
||||
if: always()
|
||||
build-macos-java:
|
||||
if: needs.config.outputs.only_job == 'build-macos-java' || (needs.config.outputs.only_job == '' && github.repository_owner == 'facebook')
|
||||
needs: config
|
||||
runs-on: macos-15-xlarge
|
||||
env:
|
||||
ROCKSDB_DISABLE_JEMALLOC: 1
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: maxim-lobanov/setup-xcode@v1.6.0
|
||||
with:
|
||||
xcode-version: 16.4.0
|
||||
- uses: "./.github/actions/setup-ccache"
|
||||
with:
|
||||
cache-key-prefix: macos-java
|
||||
- uses: "./.github/actions/increase-max-open-files-on-macos"
|
||||
- uses: "./.github/actions/install-gflags-on-macos"
|
||||
- uses: "./.github/actions/setup-jdk-on-macos"
|
||||
- uses: "./.github/actions/pre-steps-macos"
|
||||
- name: Set Java Environment
|
||||
run: |-
|
||||
echo "JAVA_HOME=${JAVA_HOME}"
|
||||
which java && java -version
|
||||
which javac && javac -version
|
||||
- name: Test RocksDBJava
|
||||
run: make V=1 J=16 -j16 jtest
|
||||
- uses: "./.github/actions/teardown-ccache"
|
||||
if: always()
|
||||
- uses: "./.github/actions/post-steps"
|
||||
build-macos-java-static:
|
||||
if: needs.config.outputs.only_job == 'build-macos-java-static' || (needs.config.outputs.only_job == '' && github.repository_owner == 'facebook')
|
||||
needs: config
|
||||
runs-on: macos-15-xlarge
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: maxim-lobanov/setup-xcode@v1.6.0
|
||||
with:
|
||||
xcode-version: 16.4.0
|
||||
- uses: "./.github/actions/setup-ccache"
|
||||
with:
|
||||
cache-key-prefix: macos-java-static
|
||||
- uses: "./.github/actions/increase-max-open-files-on-macos"
|
||||
- uses: "./.github/actions/install-gflags-on-macos"
|
||||
- uses: "./.github/actions/setup-jdk-on-macos"
|
||||
- uses: "./.github/actions/pre-steps-macos"
|
||||
- name: Set Java Environment
|
||||
run: |-
|
||||
echo "JAVA_HOME=${JAVA_HOME}"
|
||||
which java && java -version
|
||||
which javac && javac -version
|
||||
- name: Build RocksDBJava x86 and ARM Static Libraries
|
||||
run: make V=1 J=16 -j16 rocksdbjavastaticosx
|
||||
- uses: "./.github/actions/teardown-ccache"
|
||||
if: always()
|
||||
- uses: "./.github/actions/post-steps"
|
||||
build-macos-java-static-universal:
|
||||
if: needs.config.outputs.only_job == 'build-macos-java-static-universal' || (needs.config.outputs.only_job == '' && github.repository_owner == 'facebook')
|
||||
needs: config
|
||||
runs-on: macos-15-xlarge
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: maxim-lobanov/setup-xcode@v1.6.0
|
||||
with:
|
||||
xcode-version: 16.4.0
|
||||
- uses: "./.github/actions/setup-ccache"
|
||||
with:
|
||||
cache-key-prefix: macos-java-static-universal
|
||||
- uses: "./.github/actions/increase-max-open-files-on-macos"
|
||||
- uses: "./.github/actions/install-gflags-on-macos"
|
||||
- uses: "./.github/actions/setup-jdk-on-macos"
|
||||
- uses: "./.github/actions/pre-steps-macos"
|
||||
- name: Set Java Environment
|
||||
run: |-
|
||||
echo "JAVA_HOME=${JAVA_HOME}"
|
||||
which java && java -version
|
||||
which javac && javac -version
|
||||
- name: Build RocksDBJava Universal Binary Static Library
|
||||
run: make V=1 J=16 -j16 rocksdbjavastaticosx_ub
|
||||
- uses: "./.github/actions/teardown-ccache"
|
||||
if: always()
|
||||
- uses: "./.github/actions/post-steps"
|
||||
build-linux-java-pmd:
|
||||
if: needs.config.outputs.only_job == 'build-linux-java-pmd' || (needs.config.outputs.only_job == '' && github.repository_owner == 'facebook')
|
||||
needs: config
|
||||
runs-on:
|
||||
labels: 4-core-ubuntu
|
||||
container:
|
||||
image: evolvedbinary/rocksjava:alpine3_x64-be
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/install-maven"
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- name: Set Java Environment
|
||||
run: |-
|
||||
echo "JAVA_HOME=${JAVA_HOME}"
|
||||
which java && java -version
|
||||
which javac && javac -version
|
||||
- name: PMD RocksDBJava
|
||||
run: make V=1 J=8 -j8 jpmd
|
||||
- uses: actions/upload-artifact@v4.0.0
|
||||
with:
|
||||
name: pmd-report
|
||||
path: "${{ github.workspace }}/java/target/pmd.xml"
|
||||
- uses: actions/upload-artifact@v4.0.0
|
||||
with:
|
||||
name: maven-site
|
||||
path: "${{ github.workspace }}/java/target/site"
|
||||
build-linux-arm:
|
||||
if: needs.config.outputs.only_job == 'build-linux-arm' || (needs.config.outputs.only_job == '' && github.repository_owner == 'facebook')
|
||||
needs: config
|
||||
runs-on:
|
||||
labels: 4-core-ubuntu-arm
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- run: sudo apt-get update && sudo apt-get install -y build-essential ccache
|
||||
- uses: "./.github/actions/setup-ccache"
|
||||
with:
|
||||
cache-key-prefix: arm
|
||||
- run: ROCKSDBTESTS_PLATFORM_DEPENDENT=only make V=1 J=4 -j4 all_but_some_tests check_some
|
||||
- name: Print ccache stats
|
||||
run: ccache -s
|
||||
if: always()
|
||||
shell: bash
|
||||
- uses: "./.github/actions/post-steps"
|
||||
@@ -1,20 +0,0 @@
|
||||
name: facebook/rocksdb/weekly
|
||||
on:
|
||||
schedule:
|
||||
- cron: 0 9 * * 0
|
||||
workflow_dispatch:
|
||||
permissions: {}
|
||||
jobs:
|
||||
build-linux-valgrind:
|
||||
if: ${{ github.repository_owner == 'facebook' }}
|
||||
runs-on:
|
||||
labels: 16-core-ubuntu
|
||||
timeout-minutes: 840
|
||||
container:
|
||||
image: ghcr.io/facebook/rocksdb_ubuntu:22.1
|
||||
options: --shm-size=16gb
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: "./.github/actions/pre-steps"
|
||||
- run: make V=1 -j20 valgrind_test
|
||||
- uses: "./.github/actions/post-steps"
|
||||
+2
-37
@@ -1,7 +1,4 @@
|
||||
make_config.mk
|
||||
rocksdb.pc
|
||||
.build_signature
|
||||
build_tools/__pycache__/
|
||||
|
||||
*.a
|
||||
*.arc
|
||||
@@ -10,7 +7,6 @@ build_tools/__pycache__/
|
||||
*.gcda
|
||||
*.gcno
|
||||
*.o
|
||||
*.o.tmp
|
||||
*.so
|
||||
*.so.*
|
||||
*_test
|
||||
@@ -49,19 +45,13 @@ package/
|
||||
unity.a
|
||||
tags
|
||||
etags
|
||||
GPATH
|
||||
GRTAGS
|
||||
GTAGS
|
||||
rocksdb_dump
|
||||
rocksdb_undump
|
||||
db_etc2_test
|
||||
db_test2
|
||||
trace_analyzer
|
||||
trace_analyzer_test
|
||||
block_cache_trace_analyzer
|
||||
io_tracer_parser
|
||||
.DS_Store
|
||||
.vs
|
||||
.vscode
|
||||
.clangd
|
||||
|
||||
java/out
|
||||
java/target
|
||||
@@ -89,28 +79,3 @@ tp2/
|
||||
fbcode/
|
||||
fbcode
|
||||
buckifier/*.pyc
|
||||
buckifier/__pycache__
|
||||
tools/__pycache__/
|
||||
tools/c_api_gen/__pycache__/
|
||||
.arcconfig
|
||||
|
||||
compile_commands.json
|
||||
meta_gen_cpp_compile_commands.json
|
||||
clang-format-diff.py
|
||||
.py3/
|
||||
|
||||
fuzz/proto/gen/
|
||||
fuzz/crash-*
|
||||
|
||||
cmake-build-*
|
||||
third-party/folly/
|
||||
.cache
|
||||
*.sublime-*
|
||||
|
||||
# Claude Code local settings
|
||||
.claude/settings.local.json
|
||||
|
||||
tools/__pycache__/
|
||||
# Keep documentation trackable even if broader ignores match names like "*_test".
|
||||
!docs/
|
||||
!docs/**
|
||||
|
||||
+126
@@ -0,0 +1,126 @@
|
||||
dist: xenial
|
||||
language: cpp
|
||||
os:
|
||||
- linux
|
||||
- osx
|
||||
compiler:
|
||||
- clang
|
||||
- gcc
|
||||
osx_image: xcode8.3
|
||||
jdk:
|
||||
- openjdk7
|
||||
cache:
|
||||
- ccache
|
||||
- apt
|
||||
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
packages:
|
||||
- curl
|
||||
- g++-8
|
||||
- libbz2-dev
|
||||
- libgflags-dev
|
||||
- libsnappy-dev
|
||||
- mingw-w64
|
||||
- zlib1g-dev
|
||||
env:
|
||||
- TEST_GROUP=platform_dependent # 16-18 minutes
|
||||
- TEST_GROUP=1 # 33-35 minutes
|
||||
- TEST_GROUP=2 # 18-20 minutes
|
||||
- TEST_GROUP=3 # 20-22 minutes
|
||||
- TEST_GROUP=4 # 12-14 minutes
|
||||
# Run java tests
|
||||
- JOB_NAME=java_test # 4-11 minutes
|
||||
# Build ROCKSDB_LITE
|
||||
- JOB_NAME=lite_build # 3-4 minutes
|
||||
# Build examples
|
||||
- JOB_NAME=examples # 5-7 minutes
|
||||
- JOB_NAME=cmake # 3-5 minutes
|
||||
- JOB_NAME=cmake-gcc8 # 3-5 minutes
|
||||
- JOB_NAME=cmake-mingw # 3 minutes
|
||||
|
||||
matrix:
|
||||
exclude:
|
||||
- os: osx
|
||||
env: TEST_GROUP=1
|
||||
- os: osx
|
||||
env: TEST_GROUP=2
|
||||
- os: osx
|
||||
env: TEST_GROUP=3
|
||||
- os: osx
|
||||
env: TEST_GROUP=4
|
||||
- os: osx
|
||||
env: JOB_NAME=cmake-gcc8
|
||||
- os : osx
|
||||
env: JOB_NAME=cmake-mingw
|
||||
- os : linux
|
||||
compiler: clang
|
||||
- os : osx
|
||||
compiler: gcc
|
||||
|
||||
# https://docs.travis-ci.com/user/caching/#ccache-cache
|
||||
install:
|
||||
- if [ "${TRAVIS_OS_NAME}" == osx ]; then
|
||||
brew install ccache zstd lz4 snappy xz;
|
||||
PATH=$PATH:/usr/local/opt/ccache/libexec;
|
||||
fi
|
||||
- if [ "${JOB_NAME}" == cmake-gcc8 ]; then
|
||||
CC=gcc-8 && CXX=g++-8;
|
||||
fi
|
||||
- if [[ "${JOB_NAME}" == cmake* ]] && [ "${TRAVIS_OS_NAME}" == linux ]; then
|
||||
mkdir cmake-dist && curl -sfSL https://github.com/Kitware/CMake/releases/download/v3.14.5/cmake-3.14.5-Linux-x86_64.tar.gz | tar --strip-components=1 -C cmake-dist -xz && export PATH=$PWD/cmake-dist/bin:$PATH;
|
||||
fi
|
||||
- if [[ "${JOB_NAME}" == java_test ]]; then
|
||||
java -version && echo "JAVA_HOME=${JAVA_HOME}";
|
||||
fi
|
||||
|
||||
before_script:
|
||||
# Increase the maximum number of open file descriptors, since some tests use
|
||||
# more FDs than the default limit.
|
||||
- ulimit -n 8192
|
||||
|
||||
script:
|
||||
- ${CXX} --version
|
||||
- if [ `command -v ccache` ]; then ccache -C; fi
|
||||
- case $TEST_GROUP in
|
||||
platform_dependent)
|
||||
OPT=-DTRAVIS V=1 ROCKSDBTESTS_END=db_block_cache_test make -j4 all_but_some_tests check_some
|
||||
;;
|
||||
1)
|
||||
OPT=-DTRAVIS V=1 ROCKSDBTESTS_START=db_block_cache_test ROCKSDBTESTS_END=full_filter_block_test make -j4 check_some
|
||||
;;
|
||||
2)
|
||||
OPT=-DTRAVIS V=1 make -j4 tools && OPT=-DTRAVIS V=1 ROCKSDBTESTS_START=full_filter_block_test ROCKSDBTESTS_END=write_batch_with_index_test make -j4 check_some
|
||||
;;
|
||||
3)
|
||||
OPT=-DTRAVIS V=1 ROCKSDBTESTS_START=write_batch_with_index_test ROCKSDBTESTS_END=write_prepared_transaction_test make -j4 check_some
|
||||
;;
|
||||
4)
|
||||
OPT=-DTRAVIS V=1 ROCKSDBTESTS_START=write_prepared_transaction_test make -j4 check_some
|
||||
;;
|
||||
esac
|
||||
- case $JOB_NAME in
|
||||
java_test)
|
||||
OPT=-DTRAVIS V=1 make rocksdbjava jtest
|
||||
;;
|
||||
lite_build)
|
||||
OPT='-DTRAVIS -DROCKSDB_LITE' V=1 make -j4 static_lib tools
|
||||
;;
|
||||
examples)
|
||||
OPT=-DTRAVIS V=1 make -j4 static_lib && cd examples && make -j4
|
||||
;;
|
||||
cmake-mingw)
|
||||
sudo update-alternatives --set x86_64-w64-mingw32-g++ /usr/bin/x86_64-w64-mingw32-g++-posix;
|
||||
mkdir build && cd build && cmake -DJNI=1 .. -DCMAKE_C_COMPILER=x86_64-w64-mingw32-gcc -DCMAKE_CXX_COMPILER=x86_64-w64-mingw32-g++ -DCMAKE_SYSTEM_NAME=Windows && make -j4 rocksdb rocksdbjni
|
||||
;;
|
||||
cmake*)
|
||||
mkdir build && cd build && cmake -DJNI=1 .. -DCMAKE_BUILD_TYPE=Release && make -j4 rocksdb rocksdbjni
|
||||
;;
|
||||
esac
|
||||
notifications:
|
||||
email:
|
||||
- leveldb@fb.com
|
||||
webhooks:
|
||||
- https://buildtimetrend.herokuapp.com/travis
|
||||
@@ -1,9 +0,0 @@
|
||||
# Agent Instructions
|
||||
|
||||
This repository's authoritative agent instructions live in `CLAUDE.md`.
|
||||
|
||||
Read and follow [`CLAUDE.md`](./CLAUDE.md) in full before making changes or
|
||||
reviewing code in this checkout.
|
||||
|
||||
If there is any ambiguity between this file and `CLAUDE.md`, `CLAUDE.md` takes
|
||||
precedence.
|
||||
@@ -1,435 +0,0 @@
|
||||
# RocksDB Code Generation and Review Guidance
|
||||
|
||||
This document provides guidance for generating and reviewing code in the RocksDB project, derived from analysis of code review feedback across hundreds of complex merged Pull Requests. Use this as a reference when writing code with AI assistants or conducting code reviews.
|
||||
|
||||
---
|
||||
|
||||
## General Best Practices
|
||||
|
||||
### Code Quality and Maintainability
|
||||
|
||||
**Clarity and Readability:** Write clear, self-documenting code. Use meaningful variable names, add comments for complex logic, and structure code to minimize cognitive load. Avoid clever tricks that sacrifice readability for marginal performance gains unless absolutely necessary.
|
||||
|
||||
**Consistent Style:** Follow existing code style conventions. RocksDB uses `.clang-format` for formatting, specific naming conventions, and structural patterns. Deviations from these patterns are frequently flagged in reviews.
|
||||
|
||||
**Error Handling:** Ensure robust error handling throughout the codebase. Use RocksDB's `Status` type consistently, propagate errors appropriately, and avoid silently ignoring failures. Reviewers pay close attention to edge cases and failure modes.
|
||||
|
||||
### Testing Philosophy
|
||||
|
||||
**Comprehensive Coverage:** Every change should include appropriate test coverage. This includes unit tests for isolated functionality, integration tests for component interactions, and stress tests for concurrency and performance validation. Reviewers will ask for additional tests if coverage is insufficient.
|
||||
|
||||
**Edge Cases and Failure Modes:** Tests should explicitly cover edge cases, boundary conditions, and potential failure scenarios. This is especially important for changes affecting core database operations, compaction, or recovery logic.
|
||||
|
||||
**Platform-Specific Testing:** RocksDB supports multiple platforms (Linux, Windows, macOS) and compilers (GCC, Clang, MSVC). Changes should be tested across relevant platforms, particularly when touching platform-specific code or using compiler-specific features.
|
||||
|
||||
### Performance Considerations
|
||||
|
||||
**⚠️ PERFORMANCE IS CRITICAL:** RocksDB is a high-performance storage engine where every CPU cycle and memory access matters. When writing code, always evaluate from a performance perspective. This is not optional—performance-aware coding is a fundamental requirement for all contributions.
|
||||
|
||||
**Benchmarking and Profiling:** Performance claims should be backed by empirical evidence. Use RocksDB's benchmarking tools (e.g., `db_bench`) to validate improvements. Reviewers will request benchmark results for changes that could impact performance.
|
||||
|
||||
**Memory Allocation:** Minimize dynamic memory allocations, especially in hot paths. Prefer stack allocation over heap allocation. Reuse buffers when possible. Consider using arena allocators or memory pools for frequent small allocations. Every `new`, `malloc`, or container resize has a cost.
|
||||
|
||||
**Memory Copy:** Avoid unnecessary memory copies. Use move semantics, `std::string_view`, `Slice`, and pass-by-reference where appropriate. Be aware of implicit copies in STL containers and function returns. Prefer in-place operations over copy-and-modify patterns.
|
||||
|
||||
**CPU Cache Efficiency:** Design data structures and access patterns to be cache-friendly. Keep frequently accessed data together (data locality). Prefer sequential memory access over random access. Be mindful of cache line sizes (typically 64 bytes) and avoid false sharing in concurrent code. Consider struct packing and field ordering to improve cache utilization.
|
||||
|
||||
**Loop Optimization:** Look for opportunities to collapse nested loops, reduce loop overhead, and minimize branch mispredictions. Hoist invariant computations out of loops. Consider loop unrolling for tight inner loops. Batch operations when possible to amortize per-operation overhead.
|
||||
|
||||
**SIMD and Vectorization:** Leverage SIMD instructions (SSE, AVX) for data-parallel operations when appropriate. Structure data to enable auto-vectorization by the compiler. Consider explicit SIMD intrinsics for critical hot paths like checksum computation, encoding/decoding, and bulk data processing.
|
||||
|
||||
**Branch Prediction:** Minimize unpredictable branches in hot paths. Use `LIKELY`/`UNLIKELY` macros to hint branch prediction, e.g. for error cases and other rare or otherwise costly cases, but NOT for predicting popular configurations. Consider branchless alternatives for simple conditionals. Order switch cases and if-else chains by frequency.
|
||||
|
||||
**Memory and Resource Management:** Be mindful of memory allocations, especially in hot paths. Use RAII patterns, smart pointers, and RocksDB's memory management utilities appropriately.
|
||||
|
||||
**Hot Path Analysis:** When deciding how aggressively to optimize code, consider whether it's on a hot path:
|
||||
- **Hot path** (executed thousands+ times, e.g., data access, iteration, compaction loops): Performance is paramount. Apply all optimization techniques—loop collapsing, SIMD, cache optimization, pre-allocation, etc. The cost of each operation is multiplied by execution frequency.
|
||||
- **Cold path** (executed rarely, e.g., DB open, configuration parsing, error handling): Maintainability and clarity are more important. Prefer readable code over micro-optimizations. Complex optimizations here add maintenance burden with negligible performance benefit.
|
||||
- **Warm path** (moderate frequency): Balance both concerns. Use profiling data to guide optimization decisions.
|
||||
|
||||
**Avoid Premature Optimization:** While performance is critical, focus on correctness first, then optimize based on profiling data. However, be performance-aware from the start—choosing the right algorithm and data structure upfront is not premature optimization. Use the hot path analysis above to decide how much optimization effort is warranted.
|
||||
|
||||
### API Design and Compatibility
|
||||
|
||||
**Backwards Compatibility:** RocksDB maintains strong backwards compatibility guarantees. Breaking changes are rare and require extensive justification. When deprecating features, follow the project's deprecation policy (typically spanning multiple releases).
|
||||
|
||||
**API Consistency:** New APIs should be consistent with existing patterns. Use similar naming conventions, parameter ordering, and return types. Reviewers will suggest changes to improve consistency with the broader codebase.
|
||||
|
||||
**Documentation:** Public APIs must be thoroughly documented. Include usage examples, parameter descriptions, and notes on thread safety, performance characteristics, and compatibility considerations.
|
||||
|
||||
---
|
||||
|
||||
## Component-Specific Guidance
|
||||
|
||||
### Database Core (`db`)
|
||||
|
||||
The database core handles write-ahead logging (WAL), memtables, compaction, and recovery. This component receives the most scrutiny in code reviews.
|
||||
|
||||
**Concurrency and Thread Safety:** Database operations are highly concurrent. Reviewers carefully examine locking strategies, atomic operations, and memory ordering. Document synchronization assumptions clearly. Use appropriate memory ordering semantics (`acquire`/`release` vs. `seq_cst`).
|
||||
|
||||
**Compaction Logic:** Changes to compaction are complex and high-risk. Ensure that compaction logic respects configured parameters, handles edge cases (empty databases, single-file compactions), and maintains correctness under concurrent operations.
|
||||
|
||||
**Error Propagation:** Database operations can fail in many ways (I/O errors, corruption, resource exhaustion). Ensure that errors are properly propagated, logged, and handled. Avoid assertions in production code paths.
|
||||
|
||||
**Testing:** Database core changes require extensive testing, including unit tests, integration tests, and stress tests. Test with various configurations, compaction styles, and concurrent workloads.
|
||||
|
||||
### Public Headers (`include`)
|
||||
|
||||
Public headers define RocksDB's API surface. Changes here have the highest compatibility impact.
|
||||
|
||||
**API Design:** New APIs should be intuitive, consistent with existing patterns, and well-documented. Consider how the API will be used in practice and avoid adding unnecessary complexity.
|
||||
|
||||
**Backwards Compatibility:** Breaking changes to public APIs require extensive justification and a deprecation plan. Maintain ABI compatibility for bug fixes and patch releases.
|
||||
|
||||
**Documentation:** Every public API must be thoroughly documented with usage examples, parameter descriptions, and notes on thread safety and performance characteristics.
|
||||
|
||||
**Deprecation:** When deprecating APIs, follow the project's policy. Mark deprecated APIs clearly, provide migration guidance, and maintain support for at least one major release.
|
||||
|
||||
### Internal Utilities (`util`)
|
||||
|
||||
Internal utilities provide common functionality used throughout the codebase.
|
||||
|
||||
**Code Reuse:** Utilities should be general-purpose and reusable. Avoid duplicating functionality that already exists elsewhere in the codebase.
|
||||
|
||||
**Error Handling:** Utility functions should handle errors robustly and propagate them appropriately. Consider edge cases like overflow, underflow, and invalid inputs.
|
||||
|
||||
**Testing:** Utility functions should have comprehensive test coverage, including edge cases and failure modes. Consider adding death tests for assertions.
|
||||
|
||||
**Performance:** Utilities are often used in hot paths. Ensure that implementations are efficient and avoid unnecessary allocations or copies.
|
||||
|
||||
### Table Management (`table`)
|
||||
|
||||
Table management handles SST file format, block-based tables, and table readers/writers.
|
||||
|
||||
**Block Format and Checksums:** Changes to block format require extreme care. Ensure that checksums are computed and verified correctly. Test with various compression algorithms and block sizes.
|
||||
|
||||
**Iterator Correctness:** Table iterators are used throughout the codebase. Ensure that iterator semantics (Seek, Next, Prev) are correct, especially at boundaries and with deletions.
|
||||
|
||||
**Caching and Prefetching:** Table readers interact with the block cache and prefetching logic. Ensure that cache keys are unique and that prefetching respects configured limits.
|
||||
|
||||
**Performance:** Table operations are performance-critical. Benchmark changes that could impact read or write performance.
|
||||
|
||||
### Utilities (`utilities`)
|
||||
|
||||
Utilities include optional features like transactions, backup engine, and checkpoint.
|
||||
|
||||
**Feature Isolation:** Utilities should be self-contained and not introduce unnecessary dependencies on core database internals.
|
||||
|
||||
**Deprecation and Cleanup:** Legacy features are being phased out. When removing deprecated code, ensure that migration paths are documented and that users have sufficient warning.
|
||||
|
||||
**Cross-Platform Compatibility:** Utilities often interact with OS-specific APIs. Ensure that code works on all supported platforms.
|
||||
|
||||
### Options and Configuration (`options`)
|
||||
|
||||
Options define RocksDB's configuration system.
|
||||
|
||||
**Type Safety:** Use appropriate types for options (e.g., `uint32_t` for flags, scoped enums for enumerated values).
|
||||
|
||||
**Deprecation Policy:** When deprecating options, follow the project's policy. Document the deprecation, provide migration guidance, and maintain support for at least one major release.
|
||||
|
||||
**Dynamic Configuration:** Some options can be changed dynamically. Ensure that dynamic changes are thread-safe and take effect correctly.
|
||||
|
||||
**Validation:** Validate option values and provide clear error messages for invalid configurations.
|
||||
|
||||
### Cache (`cache`)
|
||||
|
||||
Cache management is critical for RocksDB's performance.
|
||||
|
||||
**Concurrency:** Cache operations are highly concurrent. Ensure that implementations are thread-safe and use appropriate synchronization primitives.
|
||||
|
||||
**Performance:** Cache operations are in the hot path. Optimize for low latency and high throughput. Benchmark changes carefully.
|
||||
|
||||
**Memory Management:** Cache implementations must manage memory carefully to avoid leaks and excessive allocations.
|
||||
|
||||
**Eviction Policies:** Changes to eviction policies should be well-tested and benchmarked to ensure they improve overall performance.
|
||||
|
||||
---
|
||||
|
||||
## Code Review Checklist
|
||||
|
||||
When reviewing RocksDB code (or preparing code for review), use this checklist:
|
||||
|
||||
### Contract Boundaries
|
||||
- [ ] Is each behavior owned by the right layer? High-level policy (for example,
|
||||
"compaction wants this I/O mode") should live at the caller/policy layer, while
|
||||
lower layers should expose generic mechanisms (for example, "open a fresh
|
||||
reader", "skip shared cache insertion", or "use these FileOptions").
|
||||
- [ ] Do comments and names describe local contracts rather than leaking a
|
||||
specific caller's rationale into reusable APIs? Generic code should not need to
|
||||
know about one current use case unless the API itself is intentionally
|
||||
use-case-specific.
|
||||
- [ ] Does each flag or parameter control one coherent behavior? If one boolean
|
||||
starts implying ownership, cache policy, I/O mode, prefetching, and caller
|
||||
identity, split it into explicit flags or an options struct.
|
||||
- [ ] Could a future caller use this lower-level API without accidentally
|
||||
inheriting assumptions from compaction, backup, user reads, or a particular
|
||||
table format? If not, tighten the contract with assertions, clearer names, or
|
||||
a narrower API.
|
||||
- [ ] Are implementation details not being used as policy signals? Prefer an
|
||||
explicit contract over inferring behavior from incidental fields such as file
|
||||
options, cache handles, or current table-reader state.
|
||||
|
||||
### Correctness
|
||||
- [ ] Does the change preserve database semantics (e.g., snapshot isolation, key ordering)?
|
||||
- [ ] Are all error cases handled appropriately?
|
||||
- [ ] Is the change thread-safe? Are synchronization primitives used correctly?
|
||||
- [ ] Are there any potential data races or deadlocks?
|
||||
|
||||
### Testing
|
||||
- [ ] Does the change include appropriate test coverage?
|
||||
- [ ] Are edge cases and failure modes tested?
|
||||
- [ ] Have the tests been run on all supported platforms?
|
||||
- [ ] Are stress tests passing?
|
||||
|
||||
### Performance
|
||||
- [ ] Are there benchmark results for performance-sensitive changes?
|
||||
- [ ] Does the change avoid unnecessary allocations or copies?
|
||||
- [ ] Are hot paths optimized appropriately?
|
||||
|
||||
### API and Compatibility
|
||||
- [ ] Is the change backwards compatible?
|
||||
- [ ] Are new APIs consistent with existing patterns?
|
||||
- [ ] Is the public API documented?
|
||||
- [ ] Are deprecated features handled according to policy?
|
||||
|
||||
### Code Quality
|
||||
- [ ] Does the code follow RocksDB's style conventions?
|
||||
- [ ] Is the code clear and maintainable?
|
||||
- [ ] Are comments and documentation sufficient?
|
||||
- [ ] Are there any code smells or anti-patterns?
|
||||
|
||||
---
|
||||
|
||||
## Common Review Feedback Patterns
|
||||
|
||||
The following patterns emerged as frequent sources of review feedback:
|
||||
|
||||
1. **Test Coverage:** Reviewers frequently request additional tests for edge cases, platform-specific behavior, and failure modes. Complex changes require comprehensive test coverage including unit tests, integration tests, and stress tests.
|
||||
|
||||
2. **Error Handling:** Ensure proper error propagation using RocksDB's `Status` type. Avoid silent failures and provide clear error messages that include context about what failed and why.
|
||||
|
||||
3. **API Design:** New APIs should be consistent with existing patterns. Use descriptive names that follow established conventions. Avoid breaking changes without strong justification and a clear deprecation plan.
|
||||
|
||||
4. **Documentation:** Public APIs must be documented with usage examples and notes on thread safety, performance characteristics, and compatibility considerations. Complex internal logic should also be well-commented.
|
||||
|
||||
5. **Performance:** Performance-sensitive changes require benchmark results to validate improvements. Use `db_bench` and other profiling tools to measure impact. Avoid premature optimization that adds complexity without measurable benefit.
|
||||
|
||||
6. **Concurrency:** Thread safety is critical in RocksDB. Document synchronization assumptions clearly. Use appropriate memory ordering semantics. Consider potential race conditions and deadlocks.
|
||||
|
||||
7. **Code Style:** Follow existing conventions for naming, formatting, and structure. Use `.clang-format` for consistent formatting. Prefer scoped enums (`enum class`) over unscoped enums.
|
||||
|
||||
8. **Backwards Compatibility:** RocksDB maintains strong compatibility guarantees. Breaking changes require extensive justification. When deprecating features, provide migration guidance and maintain support across multiple releases.
|
||||
|
||||
9. **Refactoring:** Reviewers appreciate refactoring that improves code readability and maintainability. Look for opportunities to deduplicate code and simplify complex logic.
|
||||
|
||||
10. **Platform Compatibility:** Ensure changes work correctly on all supported platforms (Linux, Windows, macOS) and with all supported compilers (GCC, Clang, MSVC).
|
||||
|
||||
11. **Contract Boundary Leaks:** When a change plumbs a new option or use-case
|
||||
specific behavior through multiple subsystems, review the call chain for
|
||||
contract leaks. Caller-specific rationale belongs at the call site or public API
|
||||
documentation; reusable layers should expose precise, layer-local capabilities.
|
||||
Watch especially for comments mentioning one caller in generic code, booleans
|
||||
that silently bundle several behaviors, and downstream code inferring policy
|
||||
from an implementation detail instead of an explicit option.
|
||||
|
||||
---
|
||||
|
||||
## Important tips
|
||||
|
||||
### Build system
|
||||
* There are 3 build system. Make for git clones, BUCK (meta internal) for hg
|
||||
clones, and CMake for some special cases.
|
||||
* When a new .cc file is added, update Makefile, CMakeLists.txt, src.mk, BUCK.
|
||||
* Don't manually edit BUCK file, after updating src.mk, run
|
||||
/usr/local/bin/python3 buckifier/buckify_rocksdb.py to update it
|
||||
* For -j in make command, use the number of CPU cores to decide it.
|
||||
|
||||
### Avoiding mixed build modes with Make (use `AUTO_CLEAN=1`)
|
||||
|
||||
Object files are written to the same paths regardless of build flags, so
|
||||
reusing objects from a prior build with different flags causes confusing
|
||||
linker errors, etc. This problem is essentially avoidable by ALWAYS using
|
||||
`AUTO_CLEAN=1 make -j<n> <something>` for manual make invocations. This
|
||||
will automatically clean object files if the build parameters/flavor have
|
||||
changed. The `build_tools/rockstest.sh` / `rocksptest.sh` helpers described
|
||||
below set `AUTO_CLEAN=1` for you.
|
||||
|
||||
### Source checks
|
||||
* Run `make check-sources` before committing. This catches non-ASCII
|
||||
characters in source files and other source-level issues that CI will
|
||||
reject. In particular, **do not use Unicode characters** (em dashes,
|
||||
smart quotes, etc.) in comments or strings -- use ASCII equivalents
|
||||
(`--` instead of em dash, `'` instead of smart quote, etc.).
|
||||
|
||||
### License headers
|
||||
* Every new source file needs a license header. For a file that does **not**
|
||||
carry an outside/third-party copyright, use the standard Meta dual-licensed
|
||||
header (the dual-license designation is required -- a bare
|
||||
"All Rights Reserved" copyright is not an acceptable open-source header):
|
||||
```
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
```
|
||||
Use a `#` comment prefix instead of `//` for shell, Python, and Makefile
|
||||
fragments.
|
||||
* Files derived from an external source (e.g. LevelDB) keep their original
|
||||
upstream copyright line in addition to the header above.
|
||||
|
||||
### RTTI and dynamic_cast
|
||||
* Production code and `db_stress` must build in **release mode
|
||||
(`-fno-rtti`)**. Do not use `dynamic_cast` anywhere except unit tests.
|
||||
Use `static_cast_with_check` from `util/cast_util.h` (validates with
|
||||
`dynamic_cast` in debug builds, plain `static_cast` in release).
|
||||
* Unit tests (`*_test.cc`) are built in debug mode with RTTI enabled.
|
||||
|
||||
### Cross-platform / portability
|
||||
Local `make` only exercises Linux with GCC/Clang, but CI
|
||||
(`.github/workflows/pr-jobs.yml` and `nightly.yml`) gates on a much wider
|
||||
matrix, so portability breaks are invisible locally until CI fails. Code must
|
||||
build (and where noted, run tests) across:
|
||||
|
||||
| Axis | Must support |
|
||||
|------|--------------|
|
||||
| OS | Linux (x86_64 + ARM), macOS, Windows |
|
||||
| Compiler | GCC, Clang (libstdc++ **and** libc++), AppleClang, **MSVC (VS2022)**, MinGW (Linux cross-compile, build-only, no gflags) |
|
||||
| Build system | Make, CMake, and BUCK (internal) -- keep all in sync (see "Build system" above) |
|
||||
| Config | release (`-fno-rtti`), `ASSERT_STATUS_CHECKED`, ASAN/UBSAN/TSAN, folly, unity build, JNI/Java |
|
||||
|
||||
Treat these as constraints to satisfy and infer the specifics from them before
|
||||
adding any system header, libc call, or compiler-specific construct. The most
|
||||
common trap: anything that compiles under GCC/Clang on Linux but not under
|
||||
**MSVC/MinGW** -- e.g. unguarded POSIX-only headers/functions (`<unistd.h>`,
|
||||
`<sys/*.h>`, `getpid`, `_exit`, ...) or GCC/Clang extensions
|
||||
(`__attribute__`, `__builtin_*`, VLAs, `alloca`). Prefer the `port::`/`Env`
|
||||
abstractions; otherwise guard with `#ifdef OS_WIN` (POSIX `<unistd.h>` ->
|
||||
Windows `<process.h>`). Because libc++ is also tested, include what you use
|
||||
rather than relying on libstdc++ transitive includes.
|
||||
|
||||
### Unit Test
|
||||
* After all of the unit tests are added, review them and try to extract common
|
||||
reusable utility functions to reduce code duplication due to copy past between
|
||||
unit tests. This should be done every time unit test is updated.
|
||||
* Don't use sleep to wait for certain events to happen. This will cause test to
|
||||
be flaky. Instead, use sync point to synchronize thread progress.
|
||||
* Cap unit test execution with 60 seconds timeout.
|
||||
* To build and run unit tests locally, prefer these helper scripts:
|
||||
* `build_tools/rocksptest.sh <test_binary> [more_binaries...] [args...]`
|
||||
builds the binary(ies) with parallel make and `AUTO_CLEAN=1` and runs
|
||||
them under gtest-parallel, sharding the test cases across CPUs. Prefer
|
||||
this whenever running more than a couple of test cases, e.g.
|
||||
`build_tools/rocksptest.sh table_test` or
|
||||
`build_tools/rocksptest.sh db_test env_test --gtest_filter=*Foo*`.
|
||||
* `build_tools/rockstest.sh <test_binary> [args...]` builds with parallel
|
||||
make and `AUTO_CLEAN=1` and runs the binary directly (serially).
|
||||
Use it only for a very small number of test cases, e.g.
|
||||
`build_tools/rockstest.sh db_test --gtest_filter=*MixedSlowdown*`.
|
||||
* After writing a test, stress-test for flakiness (AUTO_CLEAN handles the
|
||||
rebuild needed by the `COERCE_CONTEXT_SWITCH=1` flag change):
|
||||
```bash
|
||||
COERCE_CONTEXT_SWITCH=1 build_tools/rockstest.sh {test_binary} -r100 \
|
||||
--gtest_filter="*YourTestName*"
|
||||
```
|
||||
* For CI-style flaky tests that do not reproduce with `gtest_parallel.py`,
|
||||
`--gtest_repeat`, or normal coerce-mode runs, inspect
|
||||
`tools/gtest_parallel_repro.py --help`.
|
||||
|
||||
### Unit test dedup guidelines
|
||||
* Extract helper functions for repeated patterns such as object
|
||||
construction, round-trip (encode → decode → verify), and common
|
||||
assertion sequences.
|
||||
* Use table-driven tests (struct array + loop) when multiple test cases
|
||||
share the same logic but differ only in input/expected data.
|
||||
* Prefer randomized tests over exhaustive parameter permutations. Use
|
||||
`Random` from `util/random.h` (not `std::mt19937`). Use a time-based
|
||||
seed with `SCOPED_TRACE("seed=" + std::to_string(seed))` so failures
|
||||
are reproducible.
|
||||
* Keep deterministic edge-case tests separate from randomized tests
|
||||
(error paths, boundary conditions, format verification).
|
||||
* Methods only used in tests should be private with `friend class` +
|
||||
`TEST_F` fixture wrappers. In wrappers, always fully qualify the
|
||||
target method to avoid infinite recursion.
|
||||
|
||||
### Adding new public API
|
||||
Refer to claude_md/add_public_api.md
|
||||
|
||||
### Adding new option
|
||||
Refer to claude_md/add_option.md
|
||||
|
||||
### Removing deprecated option
|
||||
Refer to claude_md/remove_option.md
|
||||
|
||||
### Metrics
|
||||
* When adding a new feature, evaluate whether there is opportunity to add
|
||||
metrics. Try to avoid causing performance regression on hot path when adding
|
||||
metrics.
|
||||
|
||||
### Stress test
|
||||
* When adding a new feature, make sure stress test covers the new option.
|
||||
|
||||
### Component docs
|
||||
* For component-level design notes and implementation walkthroughs, start with
|
||||
`docs/components/index.md`.
|
||||
* Documentation under `docs/components/` is organized by subsystem in
|
||||
`docs/components/<area>/`.
|
||||
* Each subsystem directory should have an `index.md` entry point plus focused
|
||||
chapter files for deeper topics.
|
||||
|
||||
### DB bench update
|
||||
* When adding a performance related feature, support it in db_bench
|
||||
|
||||
### Adding release note
|
||||
* Release note should be kept short at high level for external user consumption.
|
||||
|
||||
### Blog posts (docs/_posts)
|
||||
* Blog post authors must be defined in `docs/_data/authors.yml` to be displayed
|
||||
|
||||
### Final verification of the change
|
||||
* Execute `AUTO_CLEAN=1 make check` to build all of the changes and execute all
|
||||
of the tests. `AUTO_CLEAN=1` ensures a clean rebuild if your previous build
|
||||
used different parameters. Note that executing all of the tests could take
|
||||
multiple minutes.
|
||||
* Run `AUTO_CLEAN=1 ASSERT_STATUS_CHECKED=1 make check` to verify all Status
|
||||
objects are properly checked. This catches missing error handling that can
|
||||
lead to silent data corruption.
|
||||
|
||||
### Monitoring make check progress
|
||||
* Use `make check-progress` to get machine-parseable JSON progress while
|
||||
`make check` is running. This is useful for Claude Code to monitor long
|
||||
builds without timeout issues.
|
||||
* Run `make check` in background, then poll progress:
|
||||
```bash
|
||||
AUTO_CLEAN=1 make check &
|
||||
# Poll periodically:
|
||||
make check-progress
|
||||
```
|
||||
* The output shows current phase and progress:
|
||||
```json
|
||||
{"status":"running","phase":"compiling","completed":300,"total":919,...}
|
||||
{"status":"running","phase":"testing","completed":1500,"total":29962,"failed":0,"percent":5,...}
|
||||
{"status":"completed","phase":"testing","completed":29962,"total":29962,"failed":0,"percent":100,...}
|
||||
```
|
||||
* Phases: `compiling` -> `linking` -> `generating` -> `testing` -> `completed`
|
||||
* Key fields: `status`, `phase`, `completed`, `total`, `failed`, `percent`
|
||||
* When tests fail, `failed_tests` array shows details (up to 10 failures):
|
||||
```json
|
||||
{"status":"running",...,"failed":3,"failed_tests":[
|
||||
{"test":"cache_test-CacheTest.Usage","exit_code":1,"signal":0,"output":"...test log..."},
|
||||
{"test":"env_test-EnvTest.Open","exit_code":0,"signal":11,"output":"...Segmentation fault..."}
|
||||
]}
|
||||
```
|
||||
* `exit_code`: non-zero means test assertion failed
|
||||
* `signal`: non-zero means test was killed (e.g., 9=SIGKILL, 6=SIGABRT, 11=SIGSEGV)
|
||||
* `output`: last 50 lines of test log including error messages and stack traces
|
||||
|
||||
### Executing benchmark using db_bench
|
||||
* Since the goal is to measure performance, we need to build a release binary
|
||||
using `AUTO_CLEAN=1 DEBUG_LEVEL=0 make db_bench`. If there is an engine
|
||||
crash due to a bug, switch back to a debug build with
|
||||
`AUTO_CLEAN=1 make dbg`; `AUTO_CLEAN=1` handles the release<->debug rebuild
|
||||
automatically.
|
||||
|
||||
### Formatting code
|
||||
* After making change, use `make format-auto` to auto-apply formatting without
|
||||
interactive prompts (Claude Code friendly).
|
||||
+305
-1005
File diff suppressed because it is too large
Load Diff
+1
-75
@@ -1,77 +1,3 @@
|
||||
# Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
In the interest of fostering an open and welcoming environment, we as
|
||||
contributors and maintainers pledge to make participation in our project and
|
||||
our community a harassment-free experience for everyone, regardless of age, body
|
||||
size, disability, ethnicity, sex characteristics, gender identity and expression,
|
||||
level of experience, education, socio-economic status, nationality, personal
|
||||
appearance, race, religion, or sexual identity and orientation.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to creating a positive environment
|
||||
include:
|
||||
|
||||
* Using welcoming and inclusive language
|
||||
* Being respectful of differing viewpoints and experiences
|
||||
* Gracefully accepting constructive criticism
|
||||
* Focusing on what is best for the community
|
||||
* Showing empathy towards other community members
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
* The use of sexualized language or imagery and unwelcome sexual attention or
|
||||
advances
|
||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or electronic
|
||||
address, without explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Our Responsibilities
|
||||
|
||||
Project maintainers are responsible for clarifying the standards of acceptable
|
||||
behavior and are expected to take appropriate and fair corrective action in
|
||||
response to any instances of unacceptable behavior.
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or
|
||||
reject comments, commits, code, wiki edits, issues, and other contributions
|
||||
that are not aligned to this Code of Conduct, or to ban temporarily or
|
||||
permanently any contributor for other behaviors that they deem inappropriate,
|
||||
threatening, offensive, or harmful.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies within all project spaces, and it also applies when
|
||||
an individual is representing the project or its community in public spaces.
|
||||
Examples of representing a project or community include using an official
|
||||
project e-mail address, posting via an official social media account, or acting
|
||||
as an appointed representative at an online or offline event. Representation of
|
||||
a project may be further defined and clarified by project maintainers.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported by contacting the project team at <opensource-conduct@fb.com>. All
|
||||
complaints will be reviewed and investigated and will result in a response that
|
||||
is deemed necessary and appropriate to the circumstances. The project team is
|
||||
obligated to maintain confidentiality with regard to the reporter of an incident.
|
||||
Further details of specific enforcement policies may be posted separately.
|
||||
|
||||
Project maintainers who do not follow or enforce the Code of Conduct in good
|
||||
faith may face temporary or permanent repercussions as determined by other
|
||||
members of the project's leadership.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
||||
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org
|
||||
|
||||
For answers to common questions about this code of conduct, see
|
||||
https://www.contributor-covenant.org/faq
|
||||
|
||||
Facebook has adopted a Code of Conduct that we expect project participants to adhere to. Please [read the full text](https://code.facebook.com/codeofconduct) so that you can understand what actions will and will not be tolerated.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# RocksDB default options change log (NO LONGER MAINTAINED)
|
||||
# RocksDB default options change log
|
||||
## Unreleased
|
||||
* delayed_write_rate takes the rate given by rate_limiter if not specified.
|
||||
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<CLToolExe>ccache_msvc_compiler.bat</CLToolExe>
|
||||
<CLToolPath>$(MSBuildThisFileDirectory)</CLToolPath>
|
||||
<UseMultiToolTask>true</UseMultiToolTask>
|
||||
<EnforceProcessCountAcrossBuilds>true</EnforceProcessCountAcrossBuilds>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
+74
-2396
File diff suppressed because it is too large
Load Diff
+30
-52
@@ -6,7 +6,7 @@ than release mode.
|
||||
|
||||
RocksDB's library should be able to compile without any dependency installed,
|
||||
although we recommend installing some compression libraries (see below).
|
||||
We do depend on newer gcc/clang with C++20 support (GCC >= 11, Clang >= 10).
|
||||
We do depend on newer gcc/clang with C++11 support.
|
||||
|
||||
There are few options when compiling RocksDB:
|
||||
|
||||
@@ -17,18 +17,15 @@ There are few options when compiling RocksDB:
|
||||
* `make check` will compile and run all the unit tests. `make check` will compile RocksDB in debug mode.
|
||||
|
||||
* `make all` will compile our static library, and all our tools and unit tests. Our tools
|
||||
depend on gflags 2.2.0 or newer. You will need to have gflags installed to run `make all`. This will compile RocksDB in debug mode. Don't
|
||||
depend on gflags. You will need to have gflags installed to run `make all`. This will compile RocksDB in debug mode. Don't
|
||||
use binaries compiled by `make all` in production.
|
||||
|
||||
* By default the binary we produce is optimized for the CPU you're compiling on
|
||||
(`-march=native` or the equivalent). To build a binary compatible with the most
|
||||
general architecture supported by your CPU and compiler, set `PORTABLE=1` for
|
||||
the build, but performance will suffer as many operations benefit from newer
|
||||
and wider instructions. In addition to `PORTABLE=0` (default) and `PORTABLE=1`,
|
||||
it can be set to an architecture name recognized by your compiler. For example,
|
||||
on 64-bit x86, a reasonable compromise is `PORTABLE=haswell` which supports
|
||||
many or most of the available optimizations while still being compatible with
|
||||
most processors made since roughly 2013.
|
||||
* By default the binary we produce is optimized for the platform you're compiling on
|
||||
(`-march=native` or the equivalent). SSE4.2 will thus be enabled automatically if your
|
||||
CPU supports it. To print a warning if your CPU does not support SSE4.2, build with
|
||||
`USE_SSE=1 make static_lib` or, if using CMake, `cmake -DFORCE_SSE42=ON`. If you want
|
||||
to build a portable binary, add `PORTABLE=1` before your make commands, like this:
|
||||
`PORTABLE=1 make static_lib`.
|
||||
|
||||
## Dependencies
|
||||
|
||||
@@ -46,21 +43,12 @@ most processors made since roughly 2013.
|
||||
command line flags processing. You can compile rocksdb library even
|
||||
if you don't have gflags installed.
|
||||
|
||||
* `make check` will also check code formatting, which requires [clang-format](https://clang.llvm.org/docs/ClangFormat.html)
|
||||
|
||||
* If you wish to build the RocksJava static target, then cmake is required for building Snappy.
|
||||
|
||||
* If you wish to run microbench (e.g, `make microbench`, `make ribbon_bench` or `cmake -DWITH_BENCHMARK=1`), Google benchmark >= 1.6.0 is needed.
|
||||
* You can do the following to install Google benchmark. These commands are copied from `./build_tools/ubuntu20_image/Dockerfile`:
|
||||
|
||||
`$ git clone --depth 1 --branch v1.7.0 https://github.com/google/benchmark.git ~/benchmark`
|
||||
|
||||
`$ cd ~/benchmark && mkdir build && cd build && cmake .. -GNinja -DCMAKE_BUILD_TYPE=Release -DBENCHMARK_ENABLE_GTEST_TESTS=0 && ninja && ninja install`
|
||||
|
||||
## Supported platforms
|
||||
|
||||
* **Linux - Ubuntu**
|
||||
* Upgrade your gcc to version at least 11 to get C++20 support.
|
||||
* Upgrade your gcc to version at least 4.8 to get C++11 support.
|
||||
* Install gflags. First, try: `sudo apt-get install libgflags-dev`
|
||||
If this doesn't work and you're using Ubuntu, here's a nice tutorial:
|
||||
(http://askubuntu.com/questions/312173/installing-gflags-12-04)
|
||||
@@ -72,12 +60,13 @@ most processors made since roughly 2013.
|
||||
* Install zstandard: `sudo apt-get install libzstd-dev`.
|
||||
|
||||
* **Linux - CentOS / RHEL**
|
||||
* Upgrade your gcc to version at least 11 to get C++20 support
|
||||
* Upgrade your gcc to version at least 4.8 to get C++11 support:
|
||||
`yum install gcc48-c++`
|
||||
* Install gflags:
|
||||
|
||||
git clone https://github.com/gflags/gflags.git
|
||||
cd gflags
|
||||
git checkout v2.2.0
|
||||
git checkout v2.0
|
||||
./configure && make && sudo make install
|
||||
|
||||
**Notice**: Once installed, please add the include path for gflags to your `CPATH` environment variable and the
|
||||
@@ -105,27 +94,19 @@ most processors made since roughly 2013.
|
||||
sudo yum install libasan
|
||||
|
||||
* Install zstandard:
|
||||
* With [EPEL](https://fedoraproject.org/wiki/EPEL):
|
||||
|
||||
sudo yum install libzstd-devel
|
||||
|
||||
* With CentOS 8:
|
||||
|
||||
sudo dnf install libzstd-devel
|
||||
|
||||
* From source:
|
||||
|
||||
wget https://github.com/facebook/zstd/archive/v1.1.3.tar.gz
|
||||
mv v1.1.3.tar.gz zstd-1.1.3.tar.gz
|
||||
tar zxvf zstd-1.1.3.tar.gz
|
||||
cd zstd-1.1.3
|
||||
make && sudo make install
|
||||
wget https://github.com/facebook/zstd/archive/v1.1.3.tar.gz
|
||||
mv v1.1.3.tar.gz zstd-1.1.3.tar.gz
|
||||
tar zxvf zstd-1.1.3.tar.gz
|
||||
cd zstd-1.1.3
|
||||
make && sudo make install
|
||||
|
||||
* **OS X**:
|
||||
* Install latest C++ compiler that supports C++20:
|
||||
* Install latest C++ compiler that supports C++ 11:
|
||||
* Update XCode: run `xcode-select --install` (or install it from XCode App's settting).
|
||||
* Install via [homebrew](http://brew.sh/).
|
||||
* If you're first time developer in MacOS, you still need to run: `xcode-select --install` in your command line.
|
||||
* run `brew tap homebrew/versions; brew install gcc48 --use-llvm` to install gcc 4.8 (or higher).
|
||||
* run `brew install rocksdb`
|
||||
|
||||
* **FreeBSD** (11.01):
|
||||
@@ -168,39 +149,35 @@ most processors made since roughly 2013.
|
||||
|
||||
* Install the dependencies for RocksDB:
|
||||
|
||||
`pkg_add gmake gflags snappy bzip2 lz4 zstd git bash findutils gnuwatch`
|
||||
pkg_add gmake gflags snappy bzip2 lz4 zstd git jdk bash findutils gnuwatch
|
||||
|
||||
* Build RocksDB from source:
|
||||
|
||||
```bash
|
||||
cd ~
|
||||
git clone https://github.com/facebook/rocksdb.git
|
||||
cd rocksdb
|
||||
gmake static_lib
|
||||
```
|
||||
|
||||
* Build RocksJava from source (optional):
|
||||
* In OpenBSD, JDK depends on XWindows system, so please check that you installed OpenBSD with `xbase` package.
|
||||
* Install dependencies : `pkg_add -v jdk%1.8`
|
||||
```bash
|
||||
|
||||
cd rocksdb
|
||||
export JAVA_HOME=/usr/local/jdk-1.8.0
|
||||
export PATH=$PATH:/usr/local/jdk-1.8.0/bin
|
||||
gmake rocksdbjava SHA256_CMD='sha256 -q'
|
||||
```
|
||||
gmake rocksdbjava
|
||||
|
||||
* **iOS**:
|
||||
* Run: `TARGET_OS=IOS make static_lib`. When building the project which uses rocksdb iOS library, make sure to define an important pre-processing macros: `IOS_CROSS_COMPILE`.
|
||||
* Run: `TARGET_OS=IOS make static_lib`. When building the project which uses rocksdb iOS library, make sure to define two important pre-processing macros: `ROCKSDB_LITE` and `IOS_CROSS_COMPILE`.
|
||||
|
||||
* **Windows** (Visual Studio 2017 to up):
|
||||
* **Windows**:
|
||||
* For building with MS Visual Studio 13 you will need Update 4 installed.
|
||||
* Read and follow the instructions at CMakeLists.txt
|
||||
* Or install via [vcpkg](https://github.com/microsoft/vcpkg)
|
||||
* Or install via [vcpkg](https://github.com/microsoft/vcpkg)
|
||||
* run `vcpkg install rocksdb:x64-windows`
|
||||
|
||||
* **AIX 6.1**
|
||||
* Install AIX Toolbox rpms with gcc
|
||||
* Use these environment variables:
|
||||
|
||||
|
||||
export PORTABLE=1
|
||||
export CC=gcc
|
||||
export AR="ar -X64"
|
||||
@@ -211,9 +188,9 @@ most processors made since roughly 2013.
|
||||
export LIBPATH=/opt/freeware/lib
|
||||
export JAVA_HOME=/usr/java8_64
|
||||
export PATH=/opt/freeware/bin:$PATH
|
||||
|
||||
|
||||
* **Solaris Sparc**
|
||||
* Install GCC 11 and higher.
|
||||
* Install GCC 4.8.2 and higher.
|
||||
* Use these environment variables:
|
||||
|
||||
export CC=gcc
|
||||
@@ -222,3 +199,4 @@ most processors made since roughly 2013.
|
||||
export EXTRA_LDFLAGS=-m64
|
||||
export PORTABLE=1
|
||||
export PLATFORM_LDFLAGS="-static-libstdc++ -static-libgcc"
|
||||
|
||||
|
||||
@@ -1,21 +1,16 @@
|
||||
This is the list of all known third-party language bindings for RocksDB. If something is missing, please open a pull request to add it.
|
||||
|
||||
* Java - https://github.com/facebook/rocksdb/tree/main/java
|
||||
* Java - https://github.com/facebook/rocksdb/tree/master/java
|
||||
* Python
|
||||
* https://github.com/rocksdict/RocksDict
|
||||
* http://python-rocksdb.readthedocs.io/en/latest/ (unmaintained)
|
||||
* http://python-rocksdb.readthedocs.io/en/latest/
|
||||
* http://pyrocksdb.readthedocs.org/en/latest/ (unmaintained)
|
||||
* Perl - https://metacpan.org/pod/RocksDB
|
||||
* Node.js - https://npmjs.org/package/rocksdb
|
||||
* Go
|
||||
* https://github.com/linxGnu/grocksdb
|
||||
* https://github.com/tecbot/gorocksdb (unmaintained)
|
||||
* Go - https://github.com/tecbot/gorocksdb
|
||||
* Ruby - http://rubygems.org/gems/rocksdb-ruby
|
||||
* Haskell - https://hackage.haskell.org/package/rocksdb-haskell
|
||||
* PHP - https://github.com/Photonios/rocksdb-php
|
||||
* C#
|
||||
* https://github.com/warrenfalk/rocksdb-sharp
|
||||
* https://github.com/curiosity-ai/rocksdb-sharp
|
||||
* C# - https://github.com/warrenfalk/rocksdb-sharp
|
||||
* Rust
|
||||
* https://github.com/pingcap/rust-rocksdb (used in production fork of https://github.com/spacejam/rust-rocksdb)
|
||||
* https://github.com/spacejam/rust-rocksdb
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
This is the list of all known third-party plugins for RocksDB. If something is missing, please open a pull request to add it.
|
||||
|
||||
* [Dedupfs](https://github.com/ajkr/dedupfs): an example for plugin developers to reference
|
||||
* [HDFS](https://github.com/riversand963/rocksdb-hdfs-env): an Env used for interacting with HDFS. Migrated from main RocksDB repo
|
||||
* [ZenFS](https://github.com/westerndigitalcorporation/zenfs): a file system for zoned block devices
|
||||
* [RADOS](https://github.com/riversand963/rocksdb-rados-env): an Env used for interacting with RADOS. Migrated from RocksDB main repo.
|
||||
* [PMEM](https://github.com/pmem/pmem-rocksdb-plugin): a collection of plugins to enable Persistent Memory on RocksDB.
|
||||
* [IPPCP](https://github.com/intel/ippcp-plugin-rocksdb): a plugin to enable encryption on RocksDB based on Intel optimized open source IPP-Crypto library.
|
||||
* [encfs](https://github.com/pegasus-kv/encfs): a plugin to enable encryption on RocksDB based on OpenSSL library.
|
||||
@@ -1,20 +1,22 @@
|
||||
## RocksDB: A Persistent Key-Value Store for Flash and RAM Storage
|
||||
|
||||
[](https://circleci.com/gh/facebook/rocksdb)
|
||||
[](https://travis-ci.org/facebook/rocksdb)
|
||||
[](https://ci.appveyor.com/project/Facebook/rocksdb/branch/master)
|
||||
[](http://140.211.168.68:8080/job/Rocksdb)
|
||||
|
||||
RocksDB is developed and maintained by Facebook Database Engineering Team.
|
||||
It is built on earlier work on [LevelDB](https://github.com/google/leveldb) by Sanjay Ghemawat (sanjay@google.com)
|
||||
and Jeff Dean (jeff@google.com)
|
||||
|
||||
This code is a library that forms the core building block for a fast
|
||||
key-value server, especially suited for storing data on flash drives.
|
||||
key value server, especially suited for storing data on flash drives.
|
||||
It has a Log-Structured-Merge-Database (LSM) design with flexible tradeoffs
|
||||
between Write-Amplification-Factor (WAF), Read-Amplification-Factor (RAF)
|
||||
and Space-Amplification-Factor (SAF). It has multi-threaded compactions,
|
||||
making it especially suitable for storing multiple terabytes of data in a
|
||||
making it specially suitable for storing multiple terabytes of data in a
|
||||
single database.
|
||||
|
||||
Start with example usage here: https://github.com/facebook/rocksdb/tree/main/examples
|
||||
Start with example usage here: https://github.com/facebook/rocksdb/tree/master/examples
|
||||
|
||||
See the [github wiki](https://github.com/facebook/rocksdb/wiki) for more explanation.
|
||||
|
||||
@@ -22,7 +24,7 @@ The public interface is in `include/`. Callers should not include or
|
||||
rely on the details of any other header files in this package. Those
|
||||
internal APIs may be changed without warning.
|
||||
|
||||
Questions and discussions are welcome on the [RocksDB Developers Public](https://www.facebook.com/groups/rocksdb.dev/) Facebook group and [email list](https://groups.google.com/g/rocksdb) on Google Groups.
|
||||
Design discussions are conducted in https://www.facebook.com/groups/rocksdb.dev/
|
||||
|
||||
## License
|
||||
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
# RocksDBLite
|
||||
|
||||
RocksDBLite is a project focused on mobile use cases, which don't need a lot of fancy things we've built for server workloads and they are very sensitive to binary size. For that reason, we added a compile flag ROCKSDB_LITE that comments out a lot of the nonessential code and keeps the binary lean.
|
||||
|
||||
Some examples of the features disabled by ROCKSDB_LITE:
|
||||
* compiled-in support for LDB tool
|
||||
* No backupable DB
|
||||
* No support for replication (which we provide in form of TransactionalIterator)
|
||||
* No advanced monitoring tools
|
||||
* No special-purpose memtables that are highly optimized for specific use cases
|
||||
* No Transactions
|
||||
|
||||
When adding a new big feature to RocksDB, please add ROCKSDB_LITE compile guard if:
|
||||
* Nobody from mobile really needs your feature,
|
||||
* Your feature is adding a lot of weight to the binary.
|
||||
|
||||
Don't add ROCKSDB_LITE compile guard if:
|
||||
* It would introduce a lot of code complexity. Compile guards make code harder to read. It's a trade-off.
|
||||
* Your feature is not adding a lot of weight.
|
||||
|
||||
If unsure, ask. :)
|
||||
@@ -15,44 +15,17 @@ At Facebook, we use RocksDB as storage engines in multiple data management servi
|
||||
|
||||
[2] https://code.facebook.com/posts/357056558062811/logdevice-a-distributed-data-store-for-logs/
|
||||
|
||||
## Bilibili
|
||||
[Bilibili](bilibili.com) [uses](https://www.alluxio.io/blog/when-ai-meets-alluxio-at-bilibili-building-an-efficient-ai-platform-for-data-preprocessing-and-model-training/) Alluxio to speed up its ML training workloads, and Alluxio uses RocksDB to store its filesystem metadata, so Bilibili uses RocksDB.
|
||||
|
||||
Bilibili's [real-time platform](https://www.alibabacloud.com/blog/architecture-and-practices-of-bilibilis-real-time-platform_596676) uses Flink, and uses RocksDB as Flink's state store.
|
||||
|
||||
## TikTok
|
||||
TikTok, or its parent company ByteDance, uses RocksDB as the storage engine for some storage systems, such as its distributed graph database [ByteGraph](https://vldb.org/pvldb/vol15/p3306-li.pdf).
|
||||
|
||||
Also, TikTok uses [Alluxio](alluxio.io) to [speed up Presto queries](https://www.alluxio.io/resources/videos/improving-presto-performance-with-alluxio-at-tiktok/), and Alluxio stores the files' metadata in RocksDB.
|
||||
|
||||
## FoundationDB
|
||||
[FoundationDB](https://www.foundationdb.org/) [uses](https://github.com/apple/foundationdb/blob/377f1f692da6ab2fe5bdac57035651db3e5fb66d/fdbserver/KeyValueStoreRocksDB.actor.cpp) RocksDB to implement a [key-value store interface](https://github.com/apple/foundationdb/blob/377f1f692da6ab2fe5bdac57035651db3e5fb66d/fdbserver/KeyValueStoreRocksDB.actor.cpp#L1127) in its server backend.
|
||||
|
||||
## Apple
|
||||
Apple [uses](https://opensource.apple.com/projects/foundationdb/) FoundationDB, so it also uses RocksDB.
|
||||
|
||||
## Snowflake
|
||||
Snowflake [uses](https://www.snowflake.com/blog/how-foundationdb-powers-snowflake-metadata-forward/) FoundationDB, so it also uses RocksDB.
|
||||
|
||||
## Microsoft
|
||||
The Bing search engine from Microsoft uses RocksDB as the storage engine for its web data platform: https://blogs.bing.com/Engineering-Blog/october-2021/RocksDB-in-Microsoft-Bing
|
||||
|
||||
## LinkedIn
|
||||
1. [Venice](https://venicedb.org/) is a derived data platform using RocksDB as its storage engine. It is LinkedIn's ML feature store, powering thousands of recommender use cases, including the Feed, Video recommendations, and People You May Know.
|
||||
2. LinkedIn's follow feed for storing user's activities. Check out the blog post: https://engineering.linkedin.com/blog/2016/03/followfeed--linkedin-s-feed-made-faster-and-smarter
|
||||
3. Apache Samza, open source framework for stream processing.
|
||||
Two different use cases at Linkedin are using RocksDB as a storage engine:
|
||||
|
||||
Learn more about LinkedIn's follow feed and Apache Samza in a Tech Talk by Ankit Gupta and Naveen Somasundaram: http://www.youtube.com/watch?v=plqVp_OnSzg
|
||||
1. LinkedIn's follow feed for storing user's activities. Check out the blog post: https://engineering.linkedin.com/blog/2016/03/followfeed--linkedin-s-feed-made-faster-and-smarter
|
||||
2. Apache Samza, open source framework for stream processing
|
||||
|
||||
Learn more about those use cases in a Tech Talk by Ankit Gupta and Naveen Somasundaram: http://www.youtube.com/watch?v=plqVp_OnSzg
|
||||
|
||||
## Yahoo
|
||||
Yahoo is using RocksDB as a storage engine for their biggest distributed data store Sherpa. Learn more about it here: http://yahooeng.tumblr.com/post/120730204806/sherpa-scales-new-heights
|
||||
|
||||
## Tencent
|
||||
[PaxosStore](https://github.com/Tencent/paxosstore) is a distributed database supporting WeChat. It uses RocksDB as its storage engine.
|
||||
|
||||
## Baidu
|
||||
[Apache Doris](http://doris.apache.org/master/en/) is a MPP analytical database engine released by Baidu. It [uses RocksDB](http://doris.apache.org/master/en/administrator-guide/operation/tablet-meta-tool.html) to manage its tablet's metadata.
|
||||
|
||||
## CockroachDB
|
||||
CockroachDB is an open-source geo-replicated transactional database. They are using RocksDB as their storage engine. Check out their github: https://github.com/cockroachdb/cockroach
|
||||
|
||||
@@ -71,7 +44,7 @@ Tango is using RocksDB as a graph storage to store all users' connection data an
|
||||
Turn is using RocksDB as a storage layer for their key/value store, serving at peak 2.4MM QPS out of different datacenters.
|
||||
Check out our RocksDB Protobuf merge operator at: https://github.com/vladb38/rocksdb_protobuf
|
||||
|
||||
## Santander UK/Cloudera Profession Services
|
||||
## Santanader UK/Cloudera Profession Services
|
||||
Check out their blog post: http://blog.cloudera.com/blog/2015/08/inside-santanders-near-real-time-data-ingest-architecture/
|
||||
|
||||
## Airbnb
|
||||
@@ -94,7 +67,7 @@ Pinterest's Object Retrieval System uses RocksDB for storage: https://www.youtub
|
||||
[VWO's](https://vwo.com/) Smart Code checker and URL helper uses RocksDB to store all the URLs where VWO's Smart Code is installed.
|
||||
|
||||
## quasardb
|
||||
[quasardb](https://www.quasardb.net) is a high-performance, distributed, transactional key-value database that integrates well with in-memory analytics engines such as Apache Spark.
|
||||
[quasardb](https://www.quasardb.net) is a high-performance, distributed, transactional key-value database that integrates well with in-memory analytics engines such as Apache Spark.
|
||||
quasardb uses a heavily tuned RocksDB as its persistence layer.
|
||||
|
||||
## Netflix
|
||||
@@ -103,18 +76,6 @@ quasardb uses a heavily tuned RocksDB as its persistence layer.
|
||||
## TiKV
|
||||
[TiKV](https://github.com/pingcap/tikv) is a GEO-replicated, high-performance, distributed, transactional key-value database. TiKV is powered by Rust and Raft. TiKV uses RocksDB as its persistence layer.
|
||||
|
||||
## TiDB
|
||||
[TiDB](https://github.com/pingcap/tidb) uses the TiKV distributed key-value database, so it uses RocksDB.
|
||||
|
||||
## PingCAP
|
||||
[PingCAP](https://www.pingcap.com/) is the company behind TiDB, its cloud database service uses RocksDB.
|
||||
|
||||
## Apache Spark
|
||||
[Spark Structured Streaming](https://docs.databricks.com/structured-streaming/rocksdb-state-store.html) uses RocksDB as the local state store.
|
||||
|
||||
## Databricks
|
||||
[Databricks](https://www.databricks.com/) [replaces AWS RDS with TiDB](https://www.pingcap.com/case-study/how-databricks-tackles-the-scalability-limit-with-a-mysql-alternative/) for scalability, so it uses RocksDB.
|
||||
|
||||
## Apache Flink
|
||||
[Apache Flink](https://flink.apache.org/news/2016/03/08/release-1.0.0.html) uses RocksDB to store state locally on a machine.
|
||||
|
||||
@@ -125,7 +86,7 @@ quasardb uses a heavily tuned RocksDB as its persistence layer.
|
||||
[Uber](http://eng.uber.com/cherami/) uses RocksDB as a durable and scalable task queue.
|
||||
|
||||
## 360 Pika
|
||||
[360](http://www.360.cn/) [Pika](https://github.com/Qihoo360/pika) is a nosql compatible with redis. With the huge amount of data stored, redis may suffer for a capacity bottleneck, and pika was born for solving it. It has widely been used in many companies.
|
||||
[360](http://www.360.cn/) [Pika](https://github.com/Qihoo360/pika) is a nosql compatible with redis. With the huge amount of data stored, redis may suffer for a capacity bottleneck, and pika was born for solving it. It has widely been widely used in many company
|
||||
|
||||
## LzLabs
|
||||
LzLabs is using RocksDB as a storage engine in their multi-database distributed framework to store application configuration and user data.
|
||||
@@ -135,37 +96,3 @@ LzLabs is using RocksDB as a storage engine in their multi-database distributed
|
||||
|
||||
## IOTA Foundation
|
||||
[IOTA Foundation](https://www.iota.org/) is using RocksDB in the [IOTA Reference Implementation (IRI)](https://github.com/iotaledger/iri) to store the local state of the Tangle. The Tangle is the first open-source distributed ledger powering the future of the Internet of Things.
|
||||
|
||||
## Avrio Project
|
||||
[Avrio Project](http://avrio-project.github.io/avrio.network/) is using RocksDB in [Avrio ](https://github.com/avrio-project/avrio) to store blocks, account balances and data and other blockchain-releated data. Avrio is a multiblockchain decentralized cryptocurrency empowering monetary transactions.
|
||||
|
||||
## Crux
|
||||
[Crux](https://github.com/juxt/crux) is a document database that uses RocksDB for local [EAV](https://en.wikipedia.org/wiki/Entity%E2%80%93attribute%E2%80%93value_model) index storage to enable point-in-time bitemporal Datalog queries. The "unbundled" architecture uses Kafka to provide horizontal scalability.
|
||||
|
||||
## Nebula Graph
|
||||
[Nebula Graph](https://github.com/vesoft-inc/nebula) is a distributed, scalable, lightning-fast, open source graph database capable of hosting super large scale graphs with dozens of billions of vertices (nodes) and trillions of edges, with milliseconds of latency.
|
||||
|
||||
## YugabyteDB
|
||||
[YugabyteDB](https://www.yugabyte.com/) is an open source, high performance, distributed SQL database that uses RocksDB as its storage layer. For more information, please see https://github.com/yugabyte/yugabyte-db/.
|
||||
|
||||
## ArangoDB
|
||||
[ArangoDB](https://www.arangodb.com/) is a native multi-model database with flexible data models for documents, graphs, and key-values, for building high performance applications using a convenient SQL-like query language or JavaScript extensions. It uses RocksDB as its storage engine.
|
||||
|
||||
## Qdrant
|
||||
[Qdrant](https://qdrant.tech/) is an open source vector database, it [uses](https://qdrant.tech/documentation/concepts/storage/) RocksDB as its persistent storage.
|
||||
|
||||
## Milvus
|
||||
[Milvus](https://milvus.io/) is an open source vector database for unstructured data. It uses RocksDB not only as one of the supported kv storage engines, but also as a message queue.
|
||||
|
||||
## Kafka
|
||||
[Kafka](https://kafka.apache.org/) is an open-source distributed event streaming platform, it uses RocksDB to store state in Kafka Streams: https://www.confluent.io/blog/how-to-tune-rocksdb-kafka-streams-state-stores-performance/.
|
||||
|
||||
## Solana Labs
|
||||
[Solana](https://github.com/solana-labs/solana) is a fast, secure, scalable, and decentralized blockchain. It uses RocksDB as the underlying storage for its ledger store.
|
||||
|
||||
## Apache Kvrocks
|
||||
|
||||
[Apache Kvrocks](https://github.com/apache/kvrocks) is an open-source distributed key-value NoSQL database built on top of RocksDB. It serves as a cost-saving and capacity-increasing alternative drop-in replacement for Redis.
|
||||
|
||||
## Others
|
||||
More databases using RocksDB can be found at [dbdb.io](https://dbdb.io/browse?embeds=rocksdb).
|
||||
|
||||
+2
-2
@@ -24,7 +24,7 @@ We strive to achieve the following goals:
|
||||
* make all unit test pass both in debug and release builds.
|
||||
* Note: latest introduction of SyncPoint seems to disable running db_test in Release.
|
||||
* make performance on par with published benchmarks accounting for HW differences
|
||||
* we would like to keep the port code inline with the main branch with no forking
|
||||
* we would like to keep the port code inline with the master branch with no forking
|
||||
|
||||
## Build system
|
||||
We have chosen CMake as a widely accepted build system to build the Windows port. It is very fast and convenient.
|
||||
@@ -66,7 +66,7 @@ We endeavored to make it functionally on par with posix_env. This means we repli
|
||||
Even though Windows provides its own efficient thread-pool implementation we chose to replicate posix logic using `std::thread` primitives. This allows anyone to quickly detect any changes within the posix source code and replicate them within windows env. This has proven to work very well. At the same time for anyone who wishes to replace the built-in thread-pool can do so using RocksDB stackable environments.
|
||||
|
||||
For disk access we implemented all of the functionality present within the posix_env which includes memory mapped files, random access, rate-limiter support etc.
|
||||
The `use_os_buffer` flag on Posix platforms currently denotes disabling read-ahead log via `fadvise` mechanism. Windows does not have `fadvise` system call. What is more, it implements disk cache in a way that differs from Linux greatly. It's not an uncommon practice on Windows to perform un-buffered disk access to gain control of the memory consumption. We think that in our use case this may also be a good configuration option at the expense of disk throughput. To compensate one may increase the configured in-memory cache size instead. Thus we have chosen `use_os_buffer=false` to disable OS disk buffering for `WinWritableFile` and `WinRandomAccessFile`. The OS imposes restrictions on the alignment of the disk offsets, buffers used and the amount of data that is read/written when accessing files in un-buffered mode. When the option is true, the classes behave in a standard way. This allows to perform writes and reads in cases when un-buffered access does not make sense such as WAL and MANIFEST.
|
||||
The `use_os_buffer` flag on Posix platforms currently denotes disabling read-ahead log via `fadvise` mechanism. Windows does not have `fadvise` system call. What is more, it implements disk cache in a way that differs from Linux greatly. It’s not an uncommon practice on Windows to perform un-buffered disk access to gain control of the memory consumption. We think that in our use case this may also be a good configuration option at the expense of disk throughput. To compensate one may increase the configured in-memory cache size instead. Thus we have chosen `use_os_buffer=false` to disable OS disk buffering for `WinWritableFile` and `WinRandomAccessFile`. The OS imposes restrictions on the alignment of the disk offsets, buffers used and the amount of data that is read/written when accessing files in un-buffered mode. When the option is true, the classes behave in a standard way. This allows to perform writes and reads in cases when un-buffered access does not make sense such as WAL and MANIFEST.
|
||||
|
||||
We have replaced `pread/pwrite` with `WriteFile/ReadFile` with `OVERLAPPED` structure so we can atomically seek to the position of the disk operation but still perform the operation synchronously. Thus we able to emulate that functionality of `pread/pwrite` reasonably well. The only difference is that the file pointer is not returned to its original position but that hardly matters given the random nature of access.
|
||||
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
version: 1.0.{build}
|
||||
|
||||
image: Visual Studio 2017
|
||||
|
||||
environment:
|
||||
JAVA_HOME: C:\Program Files\Java\jdk1.8.0
|
||||
THIRDPARTY_HOME: $(APPVEYOR_BUILD_FOLDER)\thirdparty
|
||||
SNAPPY_HOME: $(THIRDPARTY_HOME)\snappy-1.1.7
|
||||
SNAPPY_INCLUDE: $(SNAPPY_HOME);$(SNAPPY_HOME)\build
|
||||
SNAPPY_LIB_DEBUG: $(SNAPPY_HOME)\build\Debug\snappy.lib
|
||||
SNAPPY_LIB_RELEASE: $(SNAPPY_HOME)\build\Release\snappy.lib
|
||||
LZ4_HOME: $(THIRDPARTY_HOME)\lz4-1.8.3
|
||||
LZ4_INCLUDE: $(LZ4_HOME)\lib
|
||||
LZ4_LIB_DEBUG: $(LZ4_HOME)\visual\VS2010\bin\x64_Debug\liblz4_static.lib
|
||||
LZ4_LIB_RELEASE: $(LZ4_HOME)\visual\VS2010\bin\x64_Release\liblz4_static.lib
|
||||
ZSTD_HOME: $(THIRDPARTY_HOME)\zstd-1.4.0
|
||||
ZSTD_INCLUDE: $(ZSTD_HOME)\lib;$(ZSTD_HOME)\lib\dictBuilder
|
||||
ZSTD_LIB_DEBUG: $(ZSTD_HOME)\build\VS2010\bin\x64_Debug\libzstd_static.lib
|
||||
ZSTD_LIB_RELEASE: $(ZSTD_HOME)\build\VS2010\bin\x64_Release\libzstd_static.lib
|
||||
|
||||
install:
|
||||
- md %THIRDPARTY_HOME%
|
||||
- echo "Building Snappy dependency..."
|
||||
- cd %THIRDPARTY_HOME%
|
||||
- curl -fsSL -o snappy-1.1.7.zip https://github.com/google/snappy/archive/1.1.7.zip
|
||||
- unzip snappy-1.1.7.zip
|
||||
- cd snappy-1.1.7
|
||||
- mkdir build
|
||||
- cd build
|
||||
- cmake -DCMAKE_GENERATOR_PLATFORM=x64 ..
|
||||
- msbuild Snappy.sln /p:Configuration=Debug /p:Platform=x64
|
||||
- msbuild Snappy.sln /p:Configuration=Release /p:Platform=x64
|
||||
- echo "Building LZ4 dependency..."
|
||||
- cd %THIRDPARTY_HOME%
|
||||
- curl -fsSL -o lz4-1.8.3.zip https://github.com/lz4/lz4/archive/v1.8.3.zip
|
||||
- unzip lz4-1.8.3.zip
|
||||
- cd lz4-1.8.3\visual\VS2010
|
||||
- ps: $CMD="C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\Common7\IDE\devenv.com"; & $CMD lz4.sln /upgrade
|
||||
- msbuild lz4.sln /p:Configuration=Debug /p:Platform=x64
|
||||
- msbuild lz4.sln /p:Configuration=Release /p:Platform=x64
|
||||
- echo "Building ZStd dependency..."
|
||||
- cd %THIRDPARTY_HOME%
|
||||
- curl -fsSL -o zstd-1.4.0.zip https://github.com/facebook/zstd/archive/v1.4.0.zip
|
||||
- unzip zstd-1.4.0.zip
|
||||
- cd zstd-1.4.0\build\VS2010
|
||||
- ps: $CMD="C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\Common7\IDE\devenv.com"; & $CMD zstd.sln /upgrade
|
||||
- msbuild zstd.sln /p:Configuration=Debug /p:Platform=x64
|
||||
- msbuild zstd.sln /p:Configuration=Release /p:Platform=x64
|
||||
|
||||
before_build:
|
||||
- md %APPVEYOR_BUILD_FOLDER%\build
|
||||
- cd %APPVEYOR_BUILD_FOLDER%\build
|
||||
- cmake -G "Visual Studio 15 Win64" -DOPTDBG=1 -DPORTABLE=1 -DSNAPPY=1 -DLZ4=1 -DZSTD=1 -DXPRESS=1 -DJNI=1 ..
|
||||
- cd ..
|
||||
build:
|
||||
project: build\rocksdb.sln
|
||||
parallel: true
|
||||
verbosity: normal
|
||||
|
||||
test:
|
||||
|
||||
test_script:
|
||||
- ps: build_tools\run_ci_db_test.ps1 -SuiteRun db_basic_test,db_test2,db_test,env_basic_test,env_test -Concurrency 8
|
||||
|
||||
on_failure:
|
||||
- cmd: 7z a build-failed.zip %APPVEYOR_BUILD_FOLDER%\build\ && appveyor PushArtifact build-failed.zip
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Executable → Regular
+114
-278
@@ -1,34 +1,31 @@
|
||||
#!/usr/bin/env python3
|
||||
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
|
||||
|
||||
try:
|
||||
from builtins import str
|
||||
except ImportError:
|
||||
from __builtin__ import str
|
||||
import fnmatch
|
||||
from __future__ import absolute_import
|
||||
from __future__ import division
|
||||
from __future__ import print_function
|
||||
from __future__ import unicode_literals
|
||||
from targets_builder import TARGETSBuilder
|
||||
import json
|
||||
import os
|
||||
import fnmatch
|
||||
import sys
|
||||
|
||||
from targets_builder import TARGETSBuilder, LiteralValue
|
||||
|
||||
from util import ColorString
|
||||
|
||||
# This script generates BUCK file for Buck.
|
||||
# This script generates TARGETS file for Buck.
|
||||
# Buck is a build tool specifying dependencies among different build targets.
|
||||
# User can pass extra dependencies as a JSON object via command line, and this
|
||||
# script can include these dependencies in the generate BUCK file.
|
||||
# script can include these dependencies in the generate TARGETS file.
|
||||
# Usage:
|
||||
# $python3 buckifier/buckify_rocksdb.py
|
||||
# $python buckifier/buckify_rocksdb.py
|
||||
# (This generates a TARGET file without user-specified dependency for unit
|
||||
# tests.)
|
||||
# $python3 buckifier/buckify_rocksdb.py \
|
||||
# '{"fake": {
|
||||
# "extra_deps": [":test_dep", "//fakes/module:mock1"],
|
||||
# "extra_compiler_flags": ["-DFOO_BAR", "-Os"]
|
||||
# }
|
||||
# $python buckifier/buckify_rocksdb.py \
|
||||
# '{"fake": { \
|
||||
# "extra_deps": [":test_dep", "//fakes/module:mock1"], \
|
||||
# "extra_compiler_flags": ["-DROCKSDB_LITE", "-Os"], \
|
||||
# } \
|
||||
# }'
|
||||
# (Generated BUCK file has test_dep and mock1 as dependencies for RocksDB
|
||||
# (Generated TARGETS file has test_dep and mock1 as dependencies for RocksDB
|
||||
# unit tests, and will use the extra_compiler_flags to compile the unit test
|
||||
# source.)
|
||||
|
||||
@@ -42,13 +39,13 @@ def parse_src_mk(repo_path):
|
||||
src_files = {}
|
||||
for line in open(src_mk):
|
||||
line = line.strip()
|
||||
if len(line) == 0 or line[0] == "#":
|
||||
if len(line) == 0 or line[0] == '#':
|
||||
continue
|
||||
if "=" in line:
|
||||
current_src = line.split("=")[0].strip()
|
||||
if '=' in line:
|
||||
current_src = line.split('=')[0].strip()
|
||||
src_files[current_src] = []
|
||||
elif ".c" in line:
|
||||
src_path = line.split("\\")[0].strip()
|
||||
elif '.cc' in line:
|
||||
src_path = line.split('.cc')[0].strip() + '.cc'
|
||||
src_files[current_src].append(src_path)
|
||||
return src_files
|
||||
|
||||
@@ -56,316 +53,156 @@ def parse_src_mk(repo_path):
|
||||
# get all .cc / .c files
|
||||
def get_cc_files(repo_path):
|
||||
cc_files = []
|
||||
for root, _dirnames, filenames in os.walk(
|
||||
repo_path
|
||||
): # noqa: B007 T25377293 Grandfathered in
|
||||
root = root[(len(repo_path) + 1) :]
|
||||
for root, dirnames, filenames in os.walk(repo_path): # noqa: B007 T25377293 Grandfathered in
|
||||
root = root[(len(repo_path) + 1):]
|
||||
if "java" in root:
|
||||
# Skip java
|
||||
continue
|
||||
for filename in fnmatch.filter(filenames, "*.cc"):
|
||||
for filename in fnmatch.filter(filenames, '*.cc'):
|
||||
cc_files.append(os.path.join(root, filename))
|
||||
for filename in fnmatch.filter(filenames, "*.c"):
|
||||
for filename in fnmatch.filter(filenames, '*.c'):
|
||||
cc_files.append(os.path.join(root, filename))
|
||||
return cc_files
|
||||
|
||||
|
||||
# Get non_parallel tests from Makefile
|
||||
def get_non_parallel_tests(repo_path):
|
||||
# Get tests from Makefile
|
||||
def get_tests(repo_path):
|
||||
Makefile = repo_path + "/Makefile"
|
||||
|
||||
s = set({})
|
||||
# Dictionary TEST_NAME => IS_PARALLEL
|
||||
tests = {}
|
||||
|
||||
found_non_parallel_tests = False
|
||||
found_tests = False
|
||||
for line in open(Makefile):
|
||||
line = line.strip()
|
||||
if line.startswith("NON_PARALLEL_TEST ="):
|
||||
found_non_parallel_tests = True
|
||||
elif found_non_parallel_tests:
|
||||
if line.startswith("TESTS ="):
|
||||
found_tests = True
|
||||
elif found_tests:
|
||||
if line.endswith("\\"):
|
||||
# remove the trailing \
|
||||
line = line[:-1]
|
||||
line = line.strip()
|
||||
s.add(line)
|
||||
tests[line] = False
|
||||
else:
|
||||
# we consumed all the non_parallel tests
|
||||
# we consumed all the tests
|
||||
break
|
||||
|
||||
return s
|
||||
found_parallel_tests = False
|
||||
for line in open(Makefile):
|
||||
line = line.strip()
|
||||
if line.startswith("PARALLEL_TEST ="):
|
||||
found_parallel_tests = True
|
||||
elif found_parallel_tests:
|
||||
if line.endswith("\\"):
|
||||
# remove the trailing \
|
||||
line = line[:-1]
|
||||
line = line.strip()
|
||||
tests[line] = True
|
||||
else:
|
||||
# we consumed all the parallel tests
|
||||
break
|
||||
|
||||
return tests
|
||||
|
||||
|
||||
# Parse extra dependencies passed by user from command line
|
||||
def get_dependencies():
|
||||
deps_map = {"": {"extra_deps": [], "extra_compiler_flags": []}}
|
||||
deps_map = {
|
||||
''.encode('ascii'): {
|
||||
'extra_deps'.encode('ascii'): [],
|
||||
'extra_compiler_flags'.encode('ascii'): []
|
||||
}
|
||||
}
|
||||
if len(sys.argv) < 2:
|
||||
return deps_map
|
||||
|
||||
def encode_dict(data):
|
||||
rv = {}
|
||||
for k, v in data.items():
|
||||
if isinstance(v, dict):
|
||||
if isinstance(k, unicode):
|
||||
k = k.encode('ascii')
|
||||
if isinstance(v, unicode):
|
||||
v = v.encode('ascii')
|
||||
elif isinstance(v, list):
|
||||
v = [x.encode('ascii') for x in v]
|
||||
elif isinstance(v, dict):
|
||||
v = encode_dict(v)
|
||||
rv[k] = v
|
||||
return rv
|
||||
|
||||
extra_deps = json.loads(sys.argv[1], object_hook=encode_dict)
|
||||
for target_alias, deps in extra_deps.items():
|
||||
deps_map[target_alias] = deps
|
||||
return deps_map
|
||||
|
||||
|
||||
# Prepare BUCK file for buck
|
||||
def generate_buck(repo_path, deps_map):
|
||||
print(ColorString.info("Generating BUCK"))
|
||||
# Prepare TARGETS file for buck
|
||||
def generate_targets(repo_path, deps_map):
|
||||
print(ColorString.info("Generating TARGETS"))
|
||||
# parsed src.mk file
|
||||
src_mk = parse_src_mk(repo_path)
|
||||
# get all .cc files
|
||||
cc_files = get_cc_files(repo_path)
|
||||
# get non_parallel tests from Makefile
|
||||
non_parallel_tests = get_non_parallel_tests(repo_path)
|
||||
# get tests from Makefile
|
||||
tests = get_tests(repo_path)
|
||||
|
||||
if src_mk is None or cc_files is None or non_parallel_tests is None:
|
||||
if src_mk is None or cc_files is None or tests is None:
|
||||
return False
|
||||
|
||||
extra_argv = ""
|
||||
if len(sys.argv) >= 2:
|
||||
# Heuristically quote and canonicalize whitespace for inclusion
|
||||
# in how the file was generated.
|
||||
extra_argv = " '{}'".format(" ".join(sys.argv[1].split()))
|
||||
|
||||
BUCK = TARGETSBuilder("%s/BUCK" % repo_path, extra_argv)
|
||||
|
||||
# Add oncall("rocksdb_point_of_contact") at the top
|
||||
BUCK.add_oncall("rocksdb_point_of_contact")
|
||||
|
||||
TARGETS = TARGETSBuilder("%s/TARGETS" % repo_path)
|
||||
# rocksdb_lib
|
||||
BUCK.add_library(
|
||||
TARGETS.add_library(
|
||||
"rocksdb_lib",
|
||||
src_mk["LIB_SOURCES"] +
|
||||
# always add range_tree, it's only excluded on ppc64, which we don't use internally
|
||||
src_mk["RANGE_TREE_SOURCES"] + src_mk["TOOL_LIB_SOURCES"],
|
||||
deps=[
|
||||
"//folly/container:f14_hash",
|
||||
"//folly/coro:blocking_wait",
|
||||
"//folly/coro:collect",
|
||||
"//folly/coro:coroutine",
|
||||
"//folly/coro:task",
|
||||
"//folly/synchronization:distributed_mutex",
|
||||
],
|
||||
headers=LiteralValue("glob([\"**/*.h\"])")
|
||||
)
|
||||
# rocksdb_whole_archive_lib
|
||||
BUCK.add_library(
|
||||
"rocksdb_whole_archive_lib",
|
||||
[],
|
||||
deps=[
|
||||
":rocksdb_lib",
|
||||
],
|
||||
extra_external_deps="",
|
||||
link_whole=True,
|
||||
)
|
||||
# rocksdb_with_faiss_lib
|
||||
BUCK.add_library(
|
||||
"rocksdb_with_faiss_lib",
|
||||
src_mk.get("WITH_FAISS_LIB_SOURCES", []),
|
||||
deps=[
|
||||
"//faiss:faiss",
|
||||
":rocksdb_lib",
|
||||
],
|
||||
)
|
||||
src_mk["TOOL_LIB_SOURCES"])
|
||||
# rocksdb_test_lib
|
||||
BUCK.add_library(
|
||||
TARGETS.add_library(
|
||||
"rocksdb_test_lib",
|
||||
src_mk.get("MOCK_LIB_SOURCES", [])
|
||||
+ src_mk.get("TEST_LIB_SOURCES", [])
|
||||
+ src_mk.get("EXP_LIB_SOURCES", [])
|
||||
+ src_mk.get("ANALYZER_LIB_SOURCES", []),
|
||||
[":rocksdb_lib"],
|
||||
extra_test_libs=True,
|
||||
)
|
||||
# rocksdb_with_faiss_test_lib
|
||||
BUCK.add_library(
|
||||
"rocksdb_with_faiss_test_lib",
|
||||
src_mk.get("MOCK_LIB_SOURCES", [])
|
||||
+ src_mk.get("TEST_LIB_SOURCES", [])
|
||||
+ src_mk.get("EXP_LIB_SOURCES", [])
|
||||
+ src_mk.get("ANALYZER_LIB_SOURCES", []),
|
||||
deps=[
|
||||
":rocksdb_with_faiss_lib",
|
||||
],
|
||||
extra_test_libs=True,
|
||||
)
|
||||
src_mk.get("MOCK_LIB_SOURCES", []) +
|
||||
src_mk.get("TEST_LIB_SOURCES", []) +
|
||||
src_mk.get("EXP_LIB_SOURCES", []) +
|
||||
src_mk.get("ANALYZER_LIB_SOURCES", []),
|
||||
[":rocksdb_lib"])
|
||||
# rocksdb_tools_lib
|
||||
BUCK.add_library(
|
||||
TARGETS.add_library(
|
||||
"rocksdb_tools_lib",
|
||||
src_mk.get("BENCH_LIB_SOURCES", [])
|
||||
+ src_mk.get("ANALYZER_LIB_SOURCES", [])
|
||||
+ ["test_util/testutil.cc"],
|
||||
[":rocksdb_lib"],
|
||||
)
|
||||
# rocksdb_cache_bench_tools_lib
|
||||
BUCK.add_library(
|
||||
"rocksdb_cache_bench_tools_lib",
|
||||
src_mk.get("CACHE_BENCH_LIB_SOURCES", []),
|
||||
[":rocksdb_lib"],
|
||||
)
|
||||
# rocksdb_point_lock_bench_tools_lib
|
||||
BUCK.add_library(
|
||||
"rocksdb_point_lock_bench_tools_lib",
|
||||
src_mk.get("POINT_LOCK_BENCH_LIB_SOURCES", []),
|
||||
[":rocksdb_lib"],
|
||||
)
|
||||
# rocksdb_stress_lib
|
||||
BUCK.add_rocksdb_library(
|
||||
"rocksdb_stress_lib",
|
||||
src_mk.get("ANALYZER_LIB_SOURCES", [])
|
||||
+ src_mk.get("STRESS_LIB_SOURCES", [])
|
||||
+ ["test_util/testutil.cc"],
|
||||
)
|
||||
# ldb binary
|
||||
BUCK.add_binary(
|
||||
"ldb", ["tools/ldb.cc"], [":rocksdb_tools_lib"]
|
||||
)
|
||||
# db_stress binary
|
||||
BUCK.add_binary(
|
||||
"db_stress", ["db_stress_tool/db_stress.cc"], [":rocksdb_stress_lib"]
|
||||
)
|
||||
# db_bench binary
|
||||
BUCK.add_binary(
|
||||
"db_bench", ["tools/db_bench.cc"], [":rocksdb_tools_lib"]
|
||||
)
|
||||
# cache_bench binary
|
||||
BUCK.add_binary(
|
||||
"cache_bench", ["cache/cache_bench.cc"], [":rocksdb_cache_bench_tools_lib"]
|
||||
)
|
||||
# point_lock_bench binary
|
||||
BUCK.add_binary(
|
||||
"point_lock_bench",
|
||||
["utilities/transactions/lock/point/point_lock_bench.cc"],
|
||||
[":rocksdb_point_lock_bench_tools_lib"]
|
||||
)
|
||||
# bench binaries
|
||||
for src in src_mk.get("MICROBENCH_SOURCES", []):
|
||||
name = src.rsplit("/", 1)[1].split(".")[0] if "/" in src else src.split(".")[0]
|
||||
BUCK.add_binary(name, [src], [], extra_bench_libs=True)
|
||||
print(f"Extra dependencies:\n{json.dumps(deps_map)}")
|
||||
|
||||
# Dictionary test executable name -> relative source file path
|
||||
test_source_map = {}
|
||||
|
||||
# c_test.c is added through BUCK.add_c_test(). If there
|
||||
# are more than one .c test file, we need to extend
|
||||
# BUCK.add_c_test() to include other C tests too.
|
||||
for test_src in src_mk.get("TEST_MAIN_SOURCES_C", []):
|
||||
if test_src != "db/c_test.c":
|
||||
print("Don't know how to deal with " + test_src)
|
||||
return False
|
||||
BUCK.add_c_test()
|
||||
|
||||
try:
|
||||
with open(f"{repo_path}/buckifier/bench.json") as json_file:
|
||||
fast_fancy_bench_config_list = json.load(json_file)
|
||||
for config_dict in fast_fancy_bench_config_list:
|
||||
clean_benchmarks = {}
|
||||
benchmarks = config_dict["benchmarks"]
|
||||
for binary, benchmark_dict in benchmarks.items():
|
||||
clean_benchmarks[binary] = {}
|
||||
for benchmark, overloaded_metric_list in benchmark_dict.items():
|
||||
clean_benchmarks[binary][benchmark] = []
|
||||
for metric in overloaded_metric_list:
|
||||
if not isinstance(metric, dict):
|
||||
clean_benchmarks[binary][benchmark].append(metric)
|
||||
BUCK.add_fancy_bench_config(
|
||||
config_dict["name"],
|
||||
clean_benchmarks,
|
||||
False,
|
||||
config_dict["expected_runtime_one_iter"],
|
||||
config_dict["sl_iterations"],
|
||||
config_dict["regression_threshold"],
|
||||
)
|
||||
|
||||
with open(f"{repo_path}/buckifier/bench-slow.json") as json_file:
|
||||
slow_fancy_bench_config_list = json.load(json_file)
|
||||
for config_dict in slow_fancy_bench_config_list:
|
||||
clean_benchmarks = {}
|
||||
benchmarks = config_dict["benchmarks"]
|
||||
for binary, benchmark_dict in benchmarks.items():
|
||||
clean_benchmarks[binary] = {}
|
||||
for benchmark, overloaded_metric_list in benchmark_dict.items():
|
||||
clean_benchmarks[binary][benchmark] = []
|
||||
for metric in overloaded_metric_list:
|
||||
if not isinstance(metric, dict):
|
||||
clean_benchmarks[binary][benchmark].append(metric)
|
||||
for config_dict in slow_fancy_bench_config_list:
|
||||
BUCK.add_fancy_bench_config(
|
||||
config_dict["name"] + "_slow",
|
||||
clean_benchmarks,
|
||||
True,
|
||||
config_dict["expected_runtime_one_iter"],
|
||||
config_dict["sl_iterations"],
|
||||
config_dict["regression_threshold"],
|
||||
)
|
||||
# it is better servicelab experiments break
|
||||
# than rocksdb github ci
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
BUCK.add_test_header()
|
||||
|
||||
for test_src in src_mk.get("TEST_MAIN_SOURCES", []):
|
||||
test = test_src.split(".c")[0].strip().split("/")[-1].strip()
|
||||
test_source_map[test] = (test_src, False)
|
||||
print("" + test + " " + test_src)
|
||||
|
||||
for test_src in src_mk.get("WITH_FAISS_TEST_MAIN_SOURCES", []):
|
||||
test = test_src.split(".c")[0].strip().split("/")[-1].strip()
|
||||
test_source_map[test] = (test_src, True)
|
||||
print("" + test + " " + test_src + " [FAISS]")
|
||||
src_mk.get("BENCH_LIB_SOURCES", []) +
|
||||
src_mk.get("ANALYZER_LIB_SOURCES", []) +
|
||||
["test_util/testutil.cc"],
|
||||
[":rocksdb_lib"])
|
||||
|
||||
print("Extra dependencies:\n{0}".format(str(deps_map)))
|
||||
# test for every test we found in the Makefile
|
||||
for target_alias, deps in deps_map.items():
|
||||
for test, (test_src, with_faiss) in sorted(test_source_map.items()):
|
||||
if len(test) == 0:
|
||||
print(ColorString.warning("Failed to get test name for %s" % test_src))
|
||||
for test in sorted(tests):
|
||||
match_src = [src for src in cc_files if ("/%s.c" % test) in src]
|
||||
if len(match_src) == 0:
|
||||
print(ColorString.warning("Cannot find .cc file for %s" % test))
|
||||
continue
|
||||
elif len(match_src) > 1:
|
||||
print(ColorString.warning("Found more than one .cc for %s" % test))
|
||||
print(match_src)
|
||||
continue
|
||||
|
||||
test_target_name = test if not target_alias else test + "_" + target_alias
|
||||
assert(len(match_src) == 1)
|
||||
is_parallel = tests[test]
|
||||
test_target_name = \
|
||||
test if not target_alias else test + "_" + target_alias
|
||||
TARGETS.register_test(
|
||||
test_target_name,
|
||||
match_src[0],
|
||||
is_parallel,
|
||||
deps['extra_deps'],
|
||||
deps['extra_compiler_flags'])
|
||||
|
||||
if test in _EXPORTED_TEST_LIBS:
|
||||
test_library = "%s_lib" % test_target_name
|
||||
BUCK.add_library(
|
||||
test_library,
|
||||
[test_src],
|
||||
deps=[":rocksdb_test_lib"],
|
||||
extra_test_libs=True,
|
||||
)
|
||||
BUCK.register_test(
|
||||
test_target_name,
|
||||
test_src,
|
||||
deps=json.dumps(deps["extra_deps"] + [":" + test_library]),
|
||||
extra_compiler_flags=json.dumps(deps["extra_compiler_flags"]),
|
||||
)
|
||||
else:
|
||||
if with_faiss:
|
||||
BUCK.register_test(
|
||||
test_target_name,
|
||||
test_src,
|
||||
deps=json.dumps(deps["extra_deps"] + [":rocksdb_with_faiss_test_lib"]),
|
||||
extra_compiler_flags=json.dumps(deps["extra_compiler_flags"]),
|
||||
)
|
||||
else:
|
||||
BUCK.register_test(
|
||||
test_target_name,
|
||||
test_src,
|
||||
deps=json.dumps(deps["extra_deps"] + [":rocksdb_test_lib"]),
|
||||
extra_compiler_flags=json.dumps(deps["extra_compiler_flags"]),
|
||||
)
|
||||
BUCK.export_file("tools/db_crashtest.py")
|
||||
BUCK.export_file("tools/fault_injection_log_parser.py")
|
||||
TARGETS.add_library(test_library, match_src, [":rocksdb_test_lib"])
|
||||
TARGETS.flush_tests()
|
||||
|
||||
print(ColorString.info("Generated BUCK Summary:"))
|
||||
print(ColorString.info("- %d libs" % BUCK.total_lib))
|
||||
print(ColorString.info("- %d binarys" % BUCK.total_bin))
|
||||
print(ColorString.info("- %d tests" % BUCK.total_test))
|
||||
print(ColorString.info("Generated TARGETS Summary:"))
|
||||
print(ColorString.info("- %d libs" % TARGETS.total_lib))
|
||||
print(ColorString.info("- %d binarys" % TARGETS.total_bin))
|
||||
print(ColorString.info("- %d tests" % TARGETS.total_test))
|
||||
return True
|
||||
|
||||
|
||||
@@ -373,11 +210,11 @@ def get_rocksdb_path():
|
||||
# rocksdb = {script_dir}/..
|
||||
script_dir = os.path.dirname(sys.argv[0])
|
||||
script_dir = os.path.abspath(script_dir)
|
||||
rocksdb_path = os.path.abspath(os.path.join(script_dir, "../"))
|
||||
rocksdb_path = os.path.abspath(
|
||||
os.path.join(script_dir, "../"))
|
||||
|
||||
return rocksdb_path
|
||||
|
||||
|
||||
def exit_with_error(msg):
|
||||
print(ColorString.error(msg))
|
||||
sys.exit(1)
|
||||
@@ -385,11 +222,10 @@ def exit_with_error(msg):
|
||||
|
||||
def main():
|
||||
deps_map = get_dependencies()
|
||||
# Generate BUCK file for buck
|
||||
ok = generate_buck(get_rocksdb_path(), deps_map)
|
||||
# Generate TARGETS file for buck
|
||||
ok = generate_targets(get_rocksdb_path(), deps_map)
|
||||
if not ok:
|
||||
exit_with_error("Failed to generate BUCK files")
|
||||
|
||||
exit_with_error("Failed to generate TARGETS files")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
|
||||
|
||||
if [[ ! -f "BUCK" ]]
|
||||
then
|
||||
echo "BUCK file is missing!"
|
||||
echo "Please do not remove / rename BUCK file in your commit(s)."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
TGT_DIFF=`git diff BUCK | head -n 1`
|
||||
|
||||
if [ ! -z "$TGT_DIFF" ]
|
||||
then
|
||||
echo "BUCK file has uncommitted changes. Skip this check."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo Backup original BUCK file.
|
||||
|
||||
cp BUCK BUCK.bkp
|
||||
|
||||
${PYTHON:-python3} buckifier/buckify_rocksdb.py
|
||||
|
||||
if [[ ! -f "BUCK" ]]
|
||||
then
|
||||
echo "BUCK file went missing after running buckifier/buckify_rocksdb.py!"
|
||||
echo "Please do not remove the BUCK file."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
TGT_DIFF=`git diff BUCK | head -n 1`
|
||||
|
||||
if [ -z "$TGT_DIFF" ]
|
||||
then
|
||||
mv BUCK.bkp BUCK
|
||||
exit 0
|
||||
else
|
||||
echo "Please run '${PYTHON:-python3} buckifier/buckify_rocksdb.py' to update BUCK file."
|
||||
echo "Do not manually update BUCK file."
|
||||
${PYTHON:-python3} --version
|
||||
mv BUCK.bkp BUCK
|
||||
exit 1
|
||||
fi
|
||||
+48
-155
@@ -1,181 +1,74 @@
|
||||
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
|
||||
|
||||
try:
|
||||
from builtins import object, str
|
||||
except ImportError:
|
||||
from __builtin__ import object, str
|
||||
import pprint
|
||||
|
||||
from __future__ import absolute_import
|
||||
from __future__ import division
|
||||
from __future__ import print_function
|
||||
from __future__ import unicode_literals
|
||||
import targets_cfg
|
||||
|
||||
class LiteralValue:
|
||||
def __init__(self, value):
|
||||
self.value = value
|
||||
|
||||
def __str__(self):
|
||||
return str(self.value)
|
||||
|
||||
def smart_quote_value(val):
|
||||
if isinstance(val, LiteralValue):
|
||||
return str(val)
|
||||
return '"%s"' % val
|
||||
|
||||
def pretty_list(lst, indent=8):
|
||||
if lst is None or len(lst) == 0:
|
||||
return ""
|
||||
|
||||
if len(lst) == 1:
|
||||
return smart_quote_value(lst[0])
|
||||
return "\"%s\"" % lst[0]
|
||||
|
||||
separator = ',\n%s' % (" " * indent)
|
||||
res = separator.join(sorted(map(smart_quote_value, lst)))
|
||||
res = "\n" + (" " * indent) + res + ',\n' + (" " * (indent - 4))
|
||||
separator = "\",\n%s\"" % (" " * indent)
|
||||
res = separator.join(sorted(lst))
|
||||
res = "\n" + (" " * indent) + "\"" + res + "\",\n" + (" " * (indent - 4))
|
||||
return res
|
||||
|
||||
|
||||
class TARGETSBuilder:
|
||||
def __init__(self, path, extra_argv):
|
||||
def __init__(self, path):
|
||||
self.path = path
|
||||
header = targets_cfg.rocksdb_target_header_template.format(
|
||||
extra_argv=extra_argv
|
||||
)
|
||||
with open(path, "wb") as targets_file:
|
||||
targets_file.write(header.encode("utf-8"))
|
||||
self.targets_file = open(path, 'w')
|
||||
self.targets_file.write(targets_cfg.rocksdb_target_header)
|
||||
self.total_lib = 0
|
||||
self.total_bin = 0
|
||||
self.total_test = 0
|
||||
self.tests_cfg = ""
|
||||
|
||||
def add_oncall(self, oncall):
|
||||
with open(self.path, "ab") as targets_file:
|
||||
targets_file.write(targets_cfg.oncall_template.format(name=oncall).encode("utf-8"))
|
||||
|
||||
|
||||
def add_library(
|
||||
self,
|
||||
name,
|
||||
srcs,
|
||||
deps=None,
|
||||
headers=None,
|
||||
extra_external_deps="",
|
||||
link_whole=False,
|
||||
external_dependencies=None,
|
||||
extra_test_libs=False,
|
||||
):
|
||||
if headers is not None:
|
||||
if isinstance(headers, LiteralValue):
|
||||
headers = str(headers)
|
||||
else:
|
||||
headers = "[" + pretty_list(headers) + "]"
|
||||
else:
|
||||
headers = "[]"
|
||||
with open(self.path, "ab") as targets_file:
|
||||
targets_file.write(
|
||||
targets_cfg.library_template.format(
|
||||
name=name,
|
||||
srcs=pretty_list(srcs),
|
||||
headers=headers,
|
||||
deps=pretty_list(deps),
|
||||
extra_external_deps=extra_external_deps,
|
||||
link_whole=link_whole,
|
||||
external_dependencies=pretty_list(external_dependencies),
|
||||
extra_test_libs=extra_test_libs,
|
||||
).encode("utf-8")
|
||||
)
|
||||
def __del__(self):
|
||||
self.targets_file.close()
|
||||
|
||||
def add_library(self, name, srcs, deps=None, headers=None):
|
||||
headers_attr_prefix = ""
|
||||
if headers is None:
|
||||
headers_attr_prefix = "auto_"
|
||||
headers = "AutoHeaders.RECURSIVE_GLOB"
|
||||
self.targets_file.write(targets_cfg.library_template.format(
|
||||
name=name,
|
||||
srcs=pretty_list(srcs),
|
||||
headers_attr_prefix=headers_attr_prefix,
|
||||
headers=headers,
|
||||
deps=pretty_list(deps)))
|
||||
self.total_lib = self.total_lib + 1
|
||||
|
||||
def add_rocksdb_library(self, name, srcs, headers=None, external_dependencies=None):
|
||||
headers = "[" + pretty_list(headers) + "]"
|
||||
with open(self.path, "ab") as targets_file:
|
||||
targets_file.write(
|
||||
targets_cfg.rocksdb_library_template.format(
|
||||
name=name,
|
||||
srcs=pretty_list(srcs),
|
||||
headers=headers,
|
||||
external_dependencies=pretty_list(external_dependencies),
|
||||
).encode("utf-8")
|
||||
)
|
||||
self.total_lib = self.total_lib + 1
|
||||
|
||||
def add_binary(
|
||||
self,
|
||||
name,
|
||||
srcs,
|
||||
deps=None,
|
||||
extra_preprocessor_flags=None,
|
||||
extra_bench_libs=False,
|
||||
):
|
||||
with open(self.path, "ab") as targets_file:
|
||||
targets_file.write(
|
||||
targets_cfg.binary_template.format(
|
||||
name=name,
|
||||
srcs=pretty_list(srcs),
|
||||
deps=pretty_list(deps),
|
||||
extra_preprocessor_flags=pretty_list(extra_preprocessor_flags),
|
||||
extra_bench_libs=extra_bench_libs,
|
||||
).encode("utf-8")
|
||||
)
|
||||
def add_binary(self, name, srcs, deps=None):
|
||||
self.targets_file.write(targets_cfg.binary_template % (
|
||||
name,
|
||||
pretty_list(srcs),
|
||||
pretty_list(deps)))
|
||||
self.total_bin = self.total_bin + 1
|
||||
|
||||
def add_c_test(self):
|
||||
# The actual c_test_bin target is defined by add_c_test_wrapper in the
|
||||
# internal //rocks/buckifier:defs.bzl (not in this OSS repo). db/c_test.c
|
||||
# #includes the generated c_api_gen/*.inc fragments, so under Buck's
|
||||
# hermetic sandbox that wrapper must expose them as headers, e.g.
|
||||
# headers = native.glob(["c_api_gen/**/*.inc"])
|
||||
# (Make/CMake resolve the include via -I. / PROJECT_SOURCE_DIR.)
|
||||
with open(self.path, "ab") as targets_file:
|
||||
targets_file.write(
|
||||
b"""
|
||||
add_c_test_wrapper()
|
||||
"""
|
||||
)
|
||||
def register_test(self,
|
||||
test_name,
|
||||
src,
|
||||
is_parallel,
|
||||
extra_deps,
|
||||
extra_compiler_flags):
|
||||
exec_mode = "serial"
|
||||
if is_parallel:
|
||||
exec_mode = "parallel"
|
||||
self.tests_cfg += targets_cfg.test_cfg_template % (
|
||||
test_name,
|
||||
str(src),
|
||||
str(exec_mode),
|
||||
extra_deps,
|
||||
extra_compiler_flags)
|
||||
|
||||
def add_test_header(self):
|
||||
with open(self.path, "ab") as targets_file:
|
||||
targets_file.write(
|
||||
b"""
|
||||
# Generate a test rule for each entry in ROCKS_TESTS
|
||||
# Do not build the tests in opt mode, since SyncPoint and other test code
|
||||
# will not be included.
|
||||
"""
|
||||
)
|
||||
|
||||
def add_fancy_bench_config(
|
||||
self,
|
||||
name,
|
||||
bench_config,
|
||||
slow,
|
||||
expected_runtime,
|
||||
sl_iterations,
|
||||
regression_threshold,
|
||||
):
|
||||
with open(self.path, "ab") as targets_file:
|
||||
targets_file.write(
|
||||
targets_cfg.fancy_bench_template.format(
|
||||
name=name,
|
||||
bench_config=pprint.pformat(bench_config),
|
||||
slow=slow,
|
||||
expected_runtime=expected_runtime,
|
||||
sl_iterations=sl_iterations,
|
||||
regression_threshold=regression_threshold,
|
||||
).encode("utf-8")
|
||||
)
|
||||
|
||||
def register_test(self, test_name, src, deps, extra_compiler_flags):
|
||||
with open(self.path, "ab") as targets_file:
|
||||
targets_file.write(
|
||||
targets_cfg.unittests_template.format(
|
||||
test_name=test_name,
|
||||
test_cc=str(src),
|
||||
deps=deps,
|
||||
extra_compiler_flags=extra_compiler_flags,
|
||||
).encode("utf-8")
|
||||
)
|
||||
self.total_test = self.total_test + 1
|
||||
|
||||
def export_file(self, name):
|
||||
with open(self.path, "a") as targets_file:
|
||||
targets_file.write(
|
||||
targets_cfg.export_file_template.format(name=name)
|
||||
)
|
||||
def flush_tests(self):
|
||||
self.targets_file.write(targets_cfg.unittests_template % self.tests_cfg)
|
||||
self.tests_cfg = ""
|
||||
|
||||
+159
-36
@@ -1,50 +1,173 @@
|
||||
# Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
# This source code is licensed under both the GPLv2 (found in the COPYING file in the root directory)
|
||||
# and the Apache 2.0 License (found in the LICENSE.Apache file in the root directory).
|
||||
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
|
||||
from __future__ import absolute_import
|
||||
from __future__ import division
|
||||
from __future__ import print_function
|
||||
from __future__ import unicode_literals
|
||||
rocksdb_target_header = """load("@fbcode_macros//build_defs:auto_headers.bzl", "AutoHeaders")
|
||||
load("@fbcode_macros//build_defs:cpp_library.bzl", "cpp_library")
|
||||
load(":defs.bzl", "test_binary")
|
||||
|
||||
rocksdb_target_header_template = """# This file \100generated by:
|
||||
#$ python3 buckifier/buckify_rocksdb.py{extra_argv}
|
||||
# --> DO NOT EDIT MANUALLY <--
|
||||
# This file is a Meta-specific integration for buck builds, so can
|
||||
# only be validated by Meta employees.
|
||||
load("//rocks/buckifier:defs.bzl", "cpp_library_wrapper","rocks_cpp_library_wrapper","cpp_binary_wrapper","cpp_unittest_wrapper","fancy_bench_wrapper","add_c_test_wrapper")
|
||||
load("@fbcode_macros//build_defs:export_files.bzl", "export_file")
|
||||
REPO_PATH = package_name() + "/"
|
||||
|
||||
ROCKSDB_COMPILER_FLAGS = [
|
||||
"-fno-builtin-memcmp",
|
||||
# Needed to compile in fbcode
|
||||
"-Wno-expansion-to-defined",
|
||||
# Added missing flags from output of build_detect_platform
|
||||
"-Wnarrowing",
|
||||
"-DROCKSDB_NO_DYNAMIC_EXTENSION",
|
||||
]
|
||||
|
||||
ROCKSDB_EXTERNAL_DEPS = [
|
||||
("bzip2", None, "bz2"),
|
||||
("snappy", None, "snappy"),
|
||||
("zlib", None, "z"),
|
||||
("gflags", None, "gflags"),
|
||||
("lz4", None, "lz4"),
|
||||
("zstd", None),
|
||||
("tbb", None),
|
||||
("googletest", None, "gtest"),
|
||||
]
|
||||
|
||||
ROCKSDB_OS_DEPS = [
|
||||
(
|
||||
"linux",
|
||||
["third-party//numa:numa"],
|
||||
),
|
||||
]
|
||||
|
||||
ROCKSDB_OS_PREPROCESSOR_FLAGS = [
|
||||
(
|
||||
"linux",
|
||||
[
|
||||
"-DOS_LINUX",
|
||||
"-DROCKSDB_FALLOCATE_PRESENT",
|
||||
"-DROCKSDB_MALLOC_USABLE_SIZE",
|
||||
"-DROCKSDB_PTHREAD_ADAPTIVE_MUTEX",
|
||||
"-DROCKSDB_RANGESYNC_PRESENT",
|
||||
"-DROCKSDB_SCHED_GETCPU_PRESENT",
|
||||
"-DHAVE_SSE42",
|
||||
"-DNUMA",
|
||||
],
|
||||
),
|
||||
(
|
||||
"macos",
|
||||
["-DOS_MACOSX"],
|
||||
),
|
||||
]
|
||||
|
||||
ROCKSDB_PREPROCESSOR_FLAGS = [
|
||||
"-DROCKSDB_PLATFORM_POSIX",
|
||||
"-DROCKSDB_LIB_IO_POSIX",
|
||||
"-DROCKSDB_SUPPORT_THREAD_LOCAL",
|
||||
|
||||
# Flags to enable libs we include
|
||||
"-DSNAPPY",
|
||||
"-DZLIB",
|
||||
"-DBZIP2",
|
||||
"-DLZ4",
|
||||
"-DZSTD",
|
||||
"-DZSTD_STATIC_LINKING_ONLY",
|
||||
"-DGFLAGS=gflags",
|
||||
"-DTBB",
|
||||
|
||||
# Added missing flags from output of build_detect_platform
|
||||
"-DROCKSDB_BACKTRACE",
|
||||
|
||||
# Directories with files for #include
|
||||
"-I" + REPO_PATH + "include/",
|
||||
"-I" + REPO_PATH,
|
||||
]
|
||||
|
||||
ROCKSDB_ARCH_PREPROCESSOR_FLAGS = {
|
||||
"x86_64": [
|
||||
"-DHAVE_PCLMUL",
|
||||
],
|
||||
}
|
||||
|
||||
build_mode = read_config("fbcode", "build_mode")
|
||||
|
||||
is_opt_mode = build_mode.startswith("opt")
|
||||
|
||||
# -DNDEBUG is added by default in opt mode in fbcode. But adding it twice
|
||||
# doesn't harm and avoid forgetting to add it.
|
||||
ROCKSDB_COMPILER_FLAGS += (["-DNDEBUG"] if is_opt_mode else [])
|
||||
|
||||
sanitizer = read_config("fbcode", "sanitizer")
|
||||
|
||||
# Do not enable jemalloc if sanitizer presents. RocksDB will further detect
|
||||
# whether the binary is linked with jemalloc at runtime.
|
||||
ROCKSDB_OS_PREPROCESSOR_FLAGS += ([(
|
||||
"linux",
|
||||
["-DROCKSDB_JEMALLOC"],
|
||||
)] if sanitizer == "" else [])
|
||||
|
||||
ROCKSDB_OS_DEPS += ([(
|
||||
"linux",
|
||||
["third-party//jemalloc:headers"],
|
||||
)] if sanitizer == "" else [])
|
||||
"""
|
||||
|
||||
|
||||
library_template = """
|
||||
cpp_library_wrapper(name="{name}", srcs=[{srcs}], deps=[{deps}], headers={headers}, link_whole={link_whole}, extra_test_libs={extra_test_libs})
|
||||
cpp_library(
|
||||
name = "{name}",
|
||||
srcs = [{srcs}],
|
||||
{headers_attr_prefix}headers = {headers},
|
||||
arch_preprocessor_flags = ROCKSDB_ARCH_PREPROCESSOR_FLAGS,
|
||||
compiler_flags = ROCKSDB_COMPILER_FLAGS,
|
||||
os_deps = ROCKSDB_OS_DEPS,
|
||||
os_preprocessor_flags = ROCKSDB_OS_PREPROCESSOR_FLAGS,
|
||||
preprocessor_flags = ROCKSDB_PREPROCESSOR_FLAGS,
|
||||
deps = [{deps}],
|
||||
external_deps = ROCKSDB_EXTERNAL_DEPS,
|
||||
)
|
||||
"""
|
||||
|
||||
rocksdb_library_template = """
|
||||
rocks_cpp_library_wrapper(name="{name}", srcs=[{srcs}], headers={headers})
|
||||
|
||||
"""
|
||||
|
||||
|
||||
binary_template = """
|
||||
cpp_binary_wrapper(name="{name}", srcs=[{srcs}], deps=[{deps}], extra_preprocessor_flags=[{extra_preprocessor_flags}], extra_bench_libs={extra_bench_libs})
|
||||
cpp_binary(
|
||||
name = "%s",
|
||||
srcs = [%s],
|
||||
arch_preprocessor_flags = ROCKSDB_ARCH_PREPROCESSOR_FLAGS,
|
||||
compiler_flags = ROCKSDB_COMPILER_FLAGS,
|
||||
preprocessor_flags = ROCKSDB_PREPROCESSOR_FLAGS,
|
||||
deps = [%s],
|
||||
external_deps = ROCKSDB_EXTERNAL_DEPS,
|
||||
)
|
||||
"""
|
||||
|
||||
test_cfg_template = """ [
|
||||
"%s",
|
||||
"%s",
|
||||
"%s",
|
||||
%s,
|
||||
%s,
|
||||
],
|
||||
"""
|
||||
|
||||
unittests_template = """
|
||||
cpp_unittest_wrapper(name="{test_name}",
|
||||
srcs=["{test_cc}"],
|
||||
deps={deps},
|
||||
extra_compiler_flags={extra_compiler_flags})
|
||||
# [test_name, test_src, test_type, extra_deps, extra_compiler_flags]
|
||||
ROCKS_TESTS = [
|
||||
%s]
|
||||
|
||||
"""
|
||||
|
||||
fancy_bench_template = """
|
||||
fancy_bench_wrapper(suite_name="{name}", binary_to_bench_to_metric_list_map={bench_config}, slow={slow}, expected_runtime={expected_runtime}, sl_iterations={sl_iterations}, regression_threshold={regression_threshold})
|
||||
|
||||
"""
|
||||
|
||||
export_file_template = """
|
||||
export_file(name = "{name}")
|
||||
"""
|
||||
|
||||
|
||||
oncall_template = """
|
||||
oncall("{name}")
|
||||
# Generate a test rule for each entry in ROCKS_TESTS
|
||||
# Do not build the tests in opt mode, since SyncPoint and other test code
|
||||
# will not be included.
|
||||
[
|
||||
test_binary(
|
||||
extra_compiler_flags = extra_compiler_flags,
|
||||
extra_deps = extra_deps,
|
||||
parallelism = parallelism,
|
||||
rocksdb_arch_preprocessor_flags = ROCKSDB_ARCH_PREPROCESSOR_FLAGS,
|
||||
rocksdb_compiler_flags = ROCKSDB_COMPILER_FLAGS,
|
||||
rocksdb_external_deps = ROCKSDB_EXTERNAL_DEPS,
|
||||
rocksdb_os_deps = ROCKSDB_OS_DEPS,
|
||||
rocksdb_os_preprocessor_flags = ROCKSDB_OS_PREPROCESSOR_FLAGS,
|
||||
rocksdb_preprocessor_flags = ROCKSDB_PREPROCESSOR_FLAGS,
|
||||
test_cc = test_cc,
|
||||
test_name = test_name,
|
||||
)
|
||||
for test_name, test_cc, parallelism, extra_deps, extra_compiler_flags in ROCKS_TESTS
|
||||
if not is_opt_mode
|
||||
]
|
||||
"""
|
||||
|
||||
+26
-35
@@ -2,34 +2,26 @@
|
||||
"""
|
||||
This module keeps commonly used components.
|
||||
"""
|
||||
|
||||
try:
|
||||
from builtins import object
|
||||
except ImportError:
|
||||
from __builtin__ import object
|
||||
import os
|
||||
from __future__ import absolute_import
|
||||
from __future__ import division
|
||||
from __future__ import print_function
|
||||
from __future__ import unicode_literals
|
||||
import subprocess
|
||||
import sys
|
||||
import os
|
||||
import time
|
||||
|
||||
|
||||
class ColorString:
|
||||
"""Generate colorful strings on terminal"""
|
||||
|
||||
HEADER = "\033[95m"
|
||||
BLUE = "\033[94m"
|
||||
GREEN = "\033[92m"
|
||||
WARNING = "\033[93m"
|
||||
FAIL = "\033[91m"
|
||||
ENDC = "\033[0m"
|
||||
""" Generate colorful strings on terminal """
|
||||
HEADER = '\033[95m'
|
||||
BLUE = '\033[94m'
|
||||
GREEN = '\033[92m'
|
||||
WARNING = '\033[93m'
|
||||
FAIL = '\033[91m'
|
||||
ENDC = '\033[0m'
|
||||
|
||||
@staticmethod
|
||||
def _make_color_str(text, color):
|
||||
# In Python2, default encoding for unicode string is ASCII
|
||||
if sys.version_info.major <= 2:
|
||||
return "".join([color, text.encode("utf-8"), ColorString.ENDC])
|
||||
# From Python3, default encoding for unicode string is UTF-8
|
||||
return "".join([color, text, ColorString.ENDC])
|
||||
return "".join([color, text.encode('utf-8'), ColorString.ENDC])
|
||||
|
||||
@staticmethod
|
||||
def ok(text):
|
||||
@@ -65,38 +57,37 @@ class ColorString:
|
||||
|
||||
|
||||
def run_shell_command(shell_cmd, cmd_dir=None):
|
||||
"""Run a single shell command.
|
||||
@returns a tuple of shell command return code, stdout, stderr"""
|
||||
""" Run a single shell command.
|
||||
@returns a tuple of shell command return code, stdout, stderr """
|
||||
|
||||
if cmd_dir is not None and not os.path.exists(cmd_dir):
|
||||
run_shell_command("mkdir -p %s" % cmd_dir)
|
||||
|
||||
start = time.time()
|
||||
print("\t>>> Running: " + shell_cmd)
|
||||
p = subprocess.Popen( # noqa
|
||||
shell_cmd,
|
||||
shell=True,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
cwd=cmd_dir,
|
||||
)
|
||||
p = subprocess.Popen(shell_cmd,
|
||||
shell=True,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
cwd=cmd_dir)
|
||||
stdout, stderr = p.communicate()
|
||||
end = time.time()
|
||||
|
||||
# Report time if we spent more than 5 minutes executing a command
|
||||
execution_time = end - start
|
||||
if execution_time > (60 * 5):
|
||||
mins = execution_time / 60
|
||||
secs = execution_time % 60
|
||||
mins = (execution_time / 60)
|
||||
secs = (execution_time % 60)
|
||||
print("\t>time spent: %d minutes %d seconds" % (mins, secs))
|
||||
|
||||
|
||||
return p.returncode, stdout, stderr
|
||||
|
||||
|
||||
def run_shell_commands(shell_cmds, cmd_dir=None, verbose=False):
|
||||
"""Execute a sequence of shell commands, which is equivalent to
|
||||
running `cmd1 && cmd2 && cmd3`
|
||||
@returns boolean indication if all commands succeeds.
|
||||
""" Execute a sequence of shell commands, which is equivalent to
|
||||
running `cmd1 && cmd2 && cmd3`
|
||||
@returns boolean indication if all commands succeeds.
|
||||
"""
|
||||
|
||||
if cmd_dir:
|
||||
|
||||
@@ -0,0 +1,377 @@
|
||||
<?php
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
// Name of the environment variables which need to be set by the entity which
|
||||
// triggers continuous runs so that code at the end of the file gets executed
|
||||
// and Sandcastle run starts.
|
||||
const ENV_POST_RECEIVE_HOOK = "POST_RECEIVE_HOOK";
|
||||
const ENV_HTTPS_APP_VALUE = "HTTPS_APP_VALUE";
|
||||
const ENV_HTTPS_TOKEN_VALUE = "HTTPS_TOKEN_VALUE";
|
||||
|
||||
const PRIMARY_TOKEN_FILE = '/home/krad/.sandcastle';
|
||||
const CONT_RUN_ALIAS = "leveldb";
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/* Run tests in sandcastle */
|
||||
function postURL($diffID, $url) {
|
||||
assert(strlen($diffID) > 0);
|
||||
assert(is_numeric($diffID));
|
||||
assert(strlen($url) > 0);
|
||||
|
||||
$cmd_args = array(
|
||||
'diff_id' => (int)$diffID,
|
||||
'name' => sprintf(
|
||||
'click here for sandcastle tests for D%d',
|
||||
(int)$diffID
|
||||
),
|
||||
'link' => $url
|
||||
);
|
||||
$cmd = 'echo ' . escapeshellarg(json_encode($cmd_args))
|
||||
. ' | arc call-conduit differential.updateunitresults';
|
||||
|
||||
shell_exec($cmd);
|
||||
}
|
||||
|
||||
function buildUpdateTestStatusCmd($diffID, $test, $status) {
|
||||
assert(strlen($diffID) > 0);
|
||||
assert(is_numeric($diffID));
|
||||
assert(strlen($test) > 0);
|
||||
assert(strlen($status) > 0);
|
||||
|
||||
$cmd_args = array(
|
||||
'diff_id' => (int)$diffID,
|
||||
'name' => $test,
|
||||
'result' => $status
|
||||
);
|
||||
|
||||
$cmd = 'echo ' . escapeshellarg(json_encode($cmd_args))
|
||||
. ' | arc call-conduit differential.updateunitresults';
|
||||
|
||||
return $cmd;
|
||||
}
|
||||
|
||||
function updateTestStatus($diffID, $test) {
|
||||
assert(strlen($diffID) > 0);
|
||||
assert(is_numeric($diffID));
|
||||
assert(strlen($test) > 0);
|
||||
|
||||
shell_exec(buildUpdateTestStatusCmd($diffID, $test, "waiting"));
|
||||
}
|
||||
|
||||
function getSteps($applyDiff, $diffID, $username, $test) {
|
||||
assert(strlen($username) > 0);
|
||||
assert(strlen($test) > 0);
|
||||
|
||||
if ($applyDiff) {
|
||||
assert(strlen($diffID) > 0);
|
||||
assert(is_numeric($diffID));
|
||||
|
||||
$arcrc_content = (PHP_OS == "Darwin" ?
|
||||
exec("cat ~/.arcrc | gzip -f | base64") :
|
||||
exec("cat ~/.arcrc | gzip -f | base64 -w0"));
|
||||
assert(strlen($arcrc_content) > 0);
|
||||
|
||||
// Sandcastle machines don't have arc setup. We copy the user certificate
|
||||
// and authenticate using that in Sandcastle.
|
||||
$setup = array(
|
||||
"name" => "Setup arcrc",
|
||||
"shell" => "echo " . escapeshellarg($arcrc_content) . " | base64 --decode"
|
||||
. " | gzip -d > ~/.arcrc",
|
||||
"user" => "root"
|
||||
);
|
||||
|
||||
// arc demands certain permission on its config.
|
||||
// also fix the sticky bit issue in sandcastle
|
||||
$fix_permission = array(
|
||||
"name" => "Fix environment",
|
||||
"shell" => "chmod 600 ~/.arcrc && chmod +t /dev/shm",
|
||||
"user" => "root"
|
||||
);
|
||||
|
||||
// Construct the steps in the order of execution.
|
||||
$steps[] = $setup;
|
||||
$steps[] = $fix_permission;
|
||||
}
|
||||
|
||||
// fbcode is a sub-repo. We cannot patch until we add it to ignore otherwise
|
||||
// Git thinks it is an uncommitted change.
|
||||
$fix_git_ignore = array(
|
||||
"name" => "Fix git ignore",
|
||||
"shell" => "echo fbcode >> .git/info/exclude",
|
||||
"user" => "root"
|
||||
);
|
||||
|
||||
// This fixes "FATAL: ThreadSanitizer can not mmap the shadow memory"
|
||||
// Source:
|
||||
// https://github.com/google/sanitizers/wiki/ThreadSanitizerCppManual#FAQ
|
||||
$fix_kernel_issue = array(
|
||||
"name" => "Fix kernel issue with tsan",
|
||||
"shell" => "echo 2 >/proc/sys/kernel/randomize_va_space",
|
||||
"user" => "root"
|
||||
);
|
||||
|
||||
$steps[] = $fix_git_ignore;
|
||||
$steps[] = $fix_kernel_issue;
|
||||
|
||||
// This will be the command used to execute particular type of tests.
|
||||
$cmd = "";
|
||||
|
||||
if ($applyDiff) {
|
||||
// Patch the code (keep your fingures crossed).
|
||||
$patch = array(
|
||||
"name" => "Patch " . $diffID,
|
||||
"shell" => "arc --arcrc-file ~/.arcrc "
|
||||
. "patch --nocommit --diff " . escapeshellarg($diffID),
|
||||
"user" => "root"
|
||||
);
|
||||
|
||||
$steps[] = $patch;
|
||||
|
||||
updateTestStatus($diffID, $test);
|
||||
$cmd = buildUpdateTestStatusCmd($diffID, $test, "running") . "; ";
|
||||
}
|
||||
|
||||
// Run the actual command.
|
||||
$cmd = $cmd . "J=$(nproc) ./build_tools/precommit_checker.py " .
|
||||
escapeshellarg($test) . "; exit_code=$?; ";
|
||||
|
||||
if ($applyDiff) {
|
||||
$cmd = $cmd . "([[ \$exit_code -eq 0 ]] &&"
|
||||
. buildUpdateTestStatusCmd($diffID, $test, "pass") . ")"
|
||||
. "||" . buildUpdateTestStatusCmd($diffID, $test, "fail")
|
||||
. "; ";
|
||||
}
|
||||
|
||||
// shell command to sort the tests based on exit code and print
|
||||
// the output of the log files.
|
||||
$cat_sorted_logs = "
|
||||
while read code log_file;
|
||||
do echo \"################ cat \$log_file [exit_code : \$code] ################\";
|
||||
cat \$log_file;
|
||||
done < <(tail -n +2 LOG | sort -k7,7n -k4,4gr | awk '{print \$7,\$NF}')";
|
||||
|
||||
// Shell command to cat all log files
|
||||
$cat_all_logs = "for f in `ls t/!(run-*)`; do echo \$f;cat \$f; done";
|
||||
|
||||
// If LOG file exist use it to cat log files sorted by exit code, otherwise
|
||||
// cat everything
|
||||
$logs_cmd = "if [ -f LOG ]; then {$cat_sorted_logs}; else {$cat_all_logs}; fi";
|
||||
|
||||
$cmd = $cmd . " cat /tmp/precommit-check.log"
|
||||
. "; shopt -s extglob; {$logs_cmd}"
|
||||
. "; shopt -u extglob; [[ \$exit_code -eq 0 ]]";
|
||||
assert(strlen($cmd) > 0);
|
||||
|
||||
$run_test = array(
|
||||
"name" => "Run " . $test,
|
||||
"shell" => $cmd,
|
||||
"user" => "root",
|
||||
"parser" => "python build_tools/error_filter.py " . escapeshellarg($test),
|
||||
);
|
||||
|
||||
$steps[] = $run_test;
|
||||
|
||||
if ($applyDiff) {
|
||||
// Clean up the user arc config we are using.
|
||||
$cleanup = array(
|
||||
"name" => "Arc cleanup",
|
||||
"shell" => "rm -f ~/.arcrc",
|
||||
"user" => "root"
|
||||
);
|
||||
|
||||
$steps[] = $cleanup;
|
||||
}
|
||||
|
||||
assert(count($steps) > 0);
|
||||
return $steps;
|
||||
}
|
||||
|
||||
function getSandcastleConfig() {
|
||||
$sandcastle_config = array();
|
||||
|
||||
$cwd = getcwd();
|
||||
$cwd_token_file = "{$cwd}/.sandcastle";
|
||||
// This is a case when we're executed from a continuous run. Fetch the values
|
||||
// from the environment.
|
||||
if (getenv(ENV_POST_RECEIVE_HOOK)) {
|
||||
$sandcastle_config[0] = getenv(ENV_HTTPS_APP_VALUE);
|
||||
$sandcastle_config[1] = getenv(ENV_HTTPS_TOKEN_VALUE);
|
||||
} else {
|
||||
// This is a typical `[p]arc diff` case. Fetch the values from the specific
|
||||
// configuration files.
|
||||
for ($i = 0; $i < 50; $i++) {
|
||||
if (file_exists(PRIMARY_TOKEN_FILE) ||
|
||||
file_exists($cwd_token_file)) {
|
||||
break;
|
||||
}
|
||||
// If we failed to fetch the tokens, sleep for 0.2 second and try again
|
||||
usleep(200000);
|
||||
}
|
||||
assert(file_exists(PRIMARY_TOKEN_FILE) ||
|
||||
file_exists($cwd_token_file));
|
||||
|
||||
// Try the primary location first, followed by a secondary.
|
||||
if (file_exists(PRIMARY_TOKEN_FILE)) {
|
||||
$cmd = 'cat ' . PRIMARY_TOKEN_FILE;
|
||||
} else {
|
||||
$cmd = 'cat ' . escapeshellarg($cwd_token_file);
|
||||
}
|
||||
|
||||
assert(strlen($cmd) > 0);
|
||||
$sandcastle_config = explode(':', rtrim(shell_exec($cmd)));
|
||||
}
|
||||
|
||||
// In this case be very explicit about the implications.
|
||||
if (count($sandcastle_config) != 2) {
|
||||
echo "Sandcastle configuration files don't contain valid information " .
|
||||
"or the necessary environment variables aren't defined. Unable " .
|
||||
"to validate the code changes.";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
assert(strlen($sandcastle_config[0]) > 0);
|
||||
assert(strlen($sandcastle_config[1]) > 0);
|
||||
assert(count($sandcastle_config) > 0);
|
||||
|
||||
return $sandcastle_config;
|
||||
}
|
||||
|
||||
// This function can be called either from `[p]arc diff` command or during
|
||||
// the Git post-receive hook.
|
||||
function startTestsInSandcastle($applyDiff, $workflow, $diffID) {
|
||||
// Default options don't terminate on failure, but that's what we want. In
|
||||
// the current case we use assertions intentionally as "terminate on failure
|
||||
// invariants".
|
||||
assert_options(ASSERT_BAIL, true);
|
||||
|
||||
// In case of a diff we'll send notificatios to the author. Else it'll go to
|
||||
// the entire team because failures indicate that build quality has regressed.
|
||||
$username = $applyDiff ? exec("whoami") : CONT_RUN_ALIAS;
|
||||
assert(strlen($username) > 0);
|
||||
|
||||
if ($applyDiff) {
|
||||
assert($workflow);
|
||||
assert(strlen($diffID) > 0);
|
||||
assert(is_numeric($diffID));
|
||||
}
|
||||
|
||||
// List of tests we want to run in Sandcastle.
|
||||
$tests = array("unit", "unit_non_shm", "unit_481", "clang_unit", "tsan",
|
||||
"asan", "lite_test", "valgrind", "release", "release_481",
|
||||
"clang_release", "clang_analyze", "code_cov",
|
||||
"java_build", "no_compression", "unity", "ubsan");
|
||||
|
||||
$send_email_template = array(
|
||||
'type' => 'email',
|
||||
'triggers' => array('fail'),
|
||||
'emails' => array($username . '@fb.com'),
|
||||
);
|
||||
|
||||
// Construct a job definition for each test and add it to the master plan.
|
||||
foreach ($tests as $test) {
|
||||
$stepName = "RocksDB diff " . $diffID . " test " . $test;
|
||||
|
||||
if (!$applyDiff) {
|
||||
$stepName = "RocksDB continuous integration test " . $test;
|
||||
}
|
||||
|
||||
$arg[] = array(
|
||||
"name" => $stepName,
|
||||
"report" => array($send_email_template),
|
||||
"steps" => getSteps($applyDiff, $diffID, $username, $test)
|
||||
);
|
||||
}
|
||||
|
||||
// We cannot submit the parallel execution master plan to Sandcastle and
|
||||
// need supply the job plan as a determinator. So we construct a small job
|
||||
// that will spit out the master job plan which Sandcastle will parse and
|
||||
// execute. Why compress the job definitions? Otherwise we run over the max
|
||||
// string size.
|
||||
$cmd = "echo " . base64_encode(json_encode($arg))
|
||||
. (PHP_OS == "Darwin" ?
|
||||
" | gzip -f | base64" :
|
||||
" | gzip -f | base64 -w0");
|
||||
assert(strlen($cmd) > 0);
|
||||
|
||||
$arg_encoded = shell_exec($cmd);
|
||||
assert(strlen($arg_encoded) > 0);
|
||||
|
||||
$runName = "Run diff " . $diffID . "for user " . $username;
|
||||
|
||||
if (!$applyDiff) {
|
||||
$runName = "RocksDB continuous integration build and test run";
|
||||
}
|
||||
|
||||
$command = array(
|
||||
"name" => $runName,
|
||||
"steps" => array()
|
||||
);
|
||||
|
||||
$command["steps"][] = array(
|
||||
"name" => "Generate determinator",
|
||||
"shell" => "echo " . $arg_encoded . " | base64 --decode | gzip -d"
|
||||
. " | base64 --decode",
|
||||
"determinator" => true,
|
||||
"user" => "root"
|
||||
);
|
||||
|
||||
// Submit to Sandcastle.
|
||||
$url = 'https://interngraph.intern.facebook.com/sandcastle/create';
|
||||
|
||||
$job = array(
|
||||
'command' => 'SandcastleUniversalCommand',
|
||||
'args' => $command,
|
||||
'capabilities' => array(
|
||||
'vcs' => 'rocksdb-int-git',
|
||||
'type' => 'lego',
|
||||
),
|
||||
'hash' => 'origin/master',
|
||||
'user' => $username,
|
||||
'alias' => 'rocksdb-precommit',
|
||||
'tags' => array('rocksdb'),
|
||||
'description' => 'Rocksdb precommit job',
|
||||
);
|
||||
|
||||
// Fetch the configuration necessary to submit a successful HTTPS request.
|
||||
$sandcastle_config = getSandcastleConfig();
|
||||
|
||||
$app = $sandcastle_config[0];
|
||||
$token = $sandcastle_config[1];
|
||||
|
||||
$cmd = 'curl -s -k '
|
||||
. ' -F app=' . escapeshellarg($app)
|
||||
. ' -F token=' . escapeshellarg($token)
|
||||
. ' -F job=' . escapeshellarg(json_encode($job))
|
||||
.' ' . escapeshellarg($url);
|
||||
|
||||
$output = shell_exec($cmd);
|
||||
assert(strlen($output) > 0);
|
||||
|
||||
// Extract Sandcastle URL from the response.
|
||||
preg_match('/url": "(.+)"/', $output, $sandcastle_url);
|
||||
|
||||
assert(count($sandcastle_url) > 0, "Unable to submit Sandcastle request.");
|
||||
assert(strlen($sandcastle_url[1]) > 0, "Unable to extract Sandcastle URL.");
|
||||
|
||||
if ($applyDiff) {
|
||||
echo "\nSandcastle URL: " . $sandcastle_url[1] . "\n";
|
||||
// Ask Phabricator to display it on the diff UI.
|
||||
postURL($diffID, $sandcastle_url[1]);
|
||||
} else {
|
||||
echo "Continuous integration started Sandcastle tests. You can look at ";
|
||||
echo "the progress at:\n" . $sandcastle_url[1] . "\n";
|
||||
}
|
||||
}
|
||||
|
||||
// Continuous run cript will set the environment variable and based on that
|
||||
// we'll trigger the execution of tests in Sandcastle. In that case we don't
|
||||
// need to apply any diffs and there's no associated workflow either.
|
||||
if (getenv(ENV_POST_RECEIVE_HOOK)) {
|
||||
startTestsInSandcastle(
|
||||
false /* $applyDiff */,
|
||||
NULL /* $workflow */,
|
||||
NULL /* $diffID */);
|
||||
}
|
||||
+19
-75
@@ -25,17 +25,17 @@
|
||||
#
|
||||
# The solution is to move the include out of the #ifdef.
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import argparse
|
||||
from os import path
|
||||
import re
|
||||
import sys
|
||||
from os import path
|
||||
|
||||
include_re = re.compile('^[ \t]*#include[ \t]+"(.*)"[ \t]*$')
|
||||
included = set()
|
||||
excluded = set()
|
||||
|
||||
|
||||
def find_header(name, abs_path, include_paths):
|
||||
samedir = path.join(path.dirname(abs_path), name)
|
||||
if path.exists(samedir):
|
||||
@@ -46,31 +46,17 @@ def find_header(name, abs_path, include_paths):
|
||||
return include_path
|
||||
return None
|
||||
|
||||
|
||||
def expand_include(
|
||||
include_path,
|
||||
f,
|
||||
abs_path,
|
||||
source_out,
|
||||
header_out,
|
||||
include_paths,
|
||||
public_include_paths,
|
||||
):
|
||||
def expand_include(include_path, f, abs_path, source_out, header_out, include_paths, public_include_paths):
|
||||
if include_path in included:
|
||||
return False
|
||||
|
||||
included.add(include_path)
|
||||
with open(include_path) as f:
|
||||
print(f'#line 1 "{include_path}"', file=source_out)
|
||||
process_file(
|
||||
f, include_path, source_out, header_out, include_paths, public_include_paths
|
||||
)
|
||||
print('#line 1 "{}"'.format(include_path), file=source_out)
|
||||
process_file(f, include_path, source_out, header_out, include_paths, public_include_paths)
|
||||
return True
|
||||
|
||||
|
||||
def process_file(
|
||||
f, abs_path, source_out, header_out, include_paths, public_include_paths
|
||||
):
|
||||
def process_file(f, abs_path, source_out, header_out, include_paths, public_include_paths):
|
||||
for (line, text) in enumerate(f):
|
||||
m = include_re.match(text)
|
||||
if m:
|
||||
@@ -82,15 +68,7 @@ def process_file(
|
||||
source_out.write(text)
|
||||
expanded = False
|
||||
else:
|
||||
expanded = expand_include(
|
||||
include_path,
|
||||
f,
|
||||
abs_path,
|
||||
source_out,
|
||||
header_out,
|
||||
include_paths,
|
||||
public_include_paths,
|
||||
)
|
||||
expanded = expand_include(include_path, f, abs_path, source_out, header_out, include_paths, public_include_paths)
|
||||
else:
|
||||
# now try public headers
|
||||
include_path = find_header(filename, abs_path, public_include_paths)
|
||||
@@ -100,52 +78,23 @@ def process_file(
|
||||
if include_path in excluded:
|
||||
source_out.write(text)
|
||||
else:
|
||||
expand_include(
|
||||
include_path,
|
||||
f,
|
||||
abs_path,
|
||||
header_out,
|
||||
None,
|
||||
public_include_paths,
|
||||
[],
|
||||
)
|
||||
expand_include(include_path, f, abs_path, header_out, None, public_include_paths, [])
|
||||
else:
|
||||
sys.exit(
|
||||
"unable to find {}, included in {} on line {}".format(
|
||||
filename, abs_path, line
|
||||
)
|
||||
)
|
||||
sys.exit("unable to find {}, included in {} on line {}".format(filename, abs_path, line))
|
||||
|
||||
if expanded:
|
||||
print(f'#line {line + 1} "{abs_path}"', file=source_out)
|
||||
print('#line {} "{}"'.format(line+1, abs_path), file=source_out)
|
||||
elif text != "#pragma once\n":
|
||||
source_out.write(text)
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Transform a unity build into an amalgamation"
|
||||
)
|
||||
parser = argparse.ArgumentParser(description="Transform a unity build into an amalgamation")
|
||||
parser.add_argument("source", help="source file")
|
||||
parser.add_argument(
|
||||
"-I",
|
||||
action="append",
|
||||
dest="include_paths",
|
||||
help="include paths for private headers",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-i",
|
||||
action="append",
|
||||
dest="public_include_paths",
|
||||
help="include paths for public headers",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-x", action="append", dest="excluded", help="excluded header files"
|
||||
)
|
||||
parser.add_argument("-I", action="append", dest="include_paths", help="include paths for private headers")
|
||||
parser.add_argument("-i", action="append", dest="public_include_paths", help="include paths for public headers")
|
||||
parser.add_argument("-x", action="append", dest="excluded", help="excluded header files")
|
||||
parser.add_argument("-o", dest="source_out", help="output C++ file", required=True)
|
||||
parser.add_argument(
|
||||
"-H", dest="header_out", help="output C++ header file", required=True
|
||||
)
|
||||
parser.add_argument("-H", dest="header_out", help="output C++ header file", required=True)
|
||||
args = parser.parse_args()
|
||||
|
||||
include_paths = list(map(path.abspath, args.include_paths or []))
|
||||
@@ -153,15 +102,10 @@ def main():
|
||||
excluded.update(map(path.abspath, args.excluded or []))
|
||||
filename = args.source
|
||||
abs_path = path.abspath(filename)
|
||||
with open(filename) as f, open(args.source_out, "w") as source_out, open(
|
||||
args.header_out, "w"
|
||||
) as header_out:
|
||||
print(f'#line 1 "{filename}"', file=source_out)
|
||||
print(f'#include "{header_out.name}"', file=source_out)
|
||||
process_file(
|
||||
f, abs_path, source_out, header_out, include_paths, public_include_paths
|
||||
)
|
||||
|
||||
with open(filename) as f, open(args.source_out, 'w') as source_out, open(args.header_out, 'w') as header_out:
|
||||
print('#line 1 "{}"'.format(filename), file=source_out)
|
||||
print('#include "{}"'.format(header_out.name), file=source_out)
|
||||
process_file(f, abs_path, source_out, header_out, include_paths, public_include_paths)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
@@ -1,238 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||
# This source code is licensed under both the GPLv2 (found in the
|
||||
# COPYING file in the root directory) and Apache 2.0 License
|
||||
# (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
"""Access the results of benchmark runs
|
||||
Send these results on to OpenSearch graphing service
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import itertools
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
|
||||
import requests
|
||||
from dateutil import parser
|
||||
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
|
||||
|
||||
class Configuration:
|
||||
opensearch_user = os.environ["ES_USER"]
|
||||
opensearch_pass = os.environ["ES_PASS"]
|
||||
|
||||
|
||||
class BenchmarkResultException(Exception):
|
||||
def __init__(self, message, content):
|
||||
super().__init__(self, message)
|
||||
self.content = content
|
||||
|
||||
|
||||
class BenchmarkUtils:
|
||||
|
||||
expected_keys = [
|
||||
"ops_sec",
|
||||
"mb_sec",
|
||||
"lsm_sz",
|
||||
"blob_sz",
|
||||
"c_wgb",
|
||||
"w_amp",
|
||||
"c_mbps",
|
||||
"c_wsecs",
|
||||
"c_csecs",
|
||||
"b_rgb",
|
||||
"b_wgb",
|
||||
"usec_op",
|
||||
"p50",
|
||||
"p99",
|
||||
"p99.9",
|
||||
"p99.99",
|
||||
"pmax",
|
||||
"uptime",
|
||||
"stall%",
|
||||
"Nstall",
|
||||
"u_cpu",
|
||||
"s_cpu",
|
||||
"rss",
|
||||
"test",
|
||||
"date",
|
||||
"version",
|
||||
"job_id",
|
||||
]
|
||||
|
||||
def sanity_check(row):
|
||||
if "test" not in row:
|
||||
logging.debug(f"not 'test' in row: {row}")
|
||||
return False
|
||||
if row["test"] == "":
|
||||
logging.debug(f"row['test'] == '': {row}")
|
||||
return False
|
||||
if "date" not in row:
|
||||
logging.debug(f"not 'date' in row: {row}")
|
||||
return False
|
||||
if "ops_sec" not in row:
|
||||
logging.debug(f"not 'ops_sec' in row: {row}")
|
||||
return False
|
||||
try:
|
||||
_ = int(row["ops_sec"])
|
||||
except (ValueError, TypeError):
|
||||
logging.debug(f"int(row['ops_sec']): {row}")
|
||||
return False
|
||||
try:
|
||||
(_, _) = parser.parse(row["date"], fuzzy_with_tokens=True)
|
||||
except (parser.ParserError):
|
||||
logging.error(
|
||||
f"parser.parse((row['date']): not a valid format for date in row: {row}"
|
||||
)
|
||||
return False
|
||||
return True
|
||||
|
||||
def conform_opensearch(row):
|
||||
(dt, _) = parser.parse(row["date"], fuzzy_with_tokens=True)
|
||||
# create a test_date field, which was previously what was expected
|
||||
# repair the date field, which has what can be a WRONG ISO FORMAT, (no leading 0 on single-digit day-of-month)
|
||||
# e.g. 2022-07-1T00:14:55 should be 2022-07-01T00:14:55
|
||||
row["test_date"] = dt.isoformat()
|
||||
row["date"] = dt.isoformat()
|
||||
return {key.replace(".", "_"): value for key, value in row.items()}
|
||||
|
||||
|
||||
class ResultParser:
|
||||
def __init__(self, field=r"(\w|[+-:.%])+", intrafield=r"(\s)+", separator="\t"):
|
||||
self.field = re.compile(field)
|
||||
self.intra = re.compile(intrafield)
|
||||
self.sep = re.compile(separator)
|
||||
|
||||
def ignore(self, l_in: str):
|
||||
if len(l_in) == 0:
|
||||
return True
|
||||
if l_in[0:1] == "#":
|
||||
return True
|
||||
return False
|
||||
|
||||
def line(self, line_in: str):
|
||||
"""Parse a line into items
|
||||
Being clever about separators
|
||||
"""
|
||||
line = line_in
|
||||
row = []
|
||||
while line != "":
|
||||
match_item = self.field.match(line)
|
||||
if match_item:
|
||||
item = match_item.group(0)
|
||||
row.append(item)
|
||||
line = line[len(item) :]
|
||||
else:
|
||||
match_intra = self.intra.match(line)
|
||||
if match_intra:
|
||||
intra = match_intra.group(0)
|
||||
# Count the separators
|
||||
# If there are >1 then generate extra blank fields
|
||||
# White space with no true separators fakes up a single separator
|
||||
tabbed = self.sep.split(intra)
|
||||
sep_count = len(tabbed) - 1
|
||||
if sep_count == 0:
|
||||
sep_count = 1
|
||||
for _ in range(sep_count - 1):
|
||||
row.append("")
|
||||
line = line[len(intra) :]
|
||||
else:
|
||||
raise BenchmarkResultException(
|
||||
"Invalid TSV line", f"{line_in} at {line}"
|
||||
)
|
||||
return row
|
||||
|
||||
def parse(self, lines):
|
||||
"""Parse something that iterates lines"""
|
||||
rows = [self.line(line) for line in lines if not self.ignore(line)]
|
||||
header = rows[0]
|
||||
width = len(header)
|
||||
records = [
|
||||
{k: v for (k, v) in itertools.zip_longest(header, row[:width])}
|
||||
for row in rows[1:]
|
||||
]
|
||||
return records
|
||||
|
||||
|
||||
def load_report_from_tsv(filename: str):
|
||||
file = open(filename)
|
||||
contents = file.readlines()
|
||||
file.close()
|
||||
parser = ResultParser()
|
||||
report = parser.parse(contents)
|
||||
logging.debug(f"Loaded TSV Report: {report}")
|
||||
return report
|
||||
|
||||
|
||||
def push_report_to_opensearch(report, esdocument):
|
||||
sanitized = [
|
||||
BenchmarkUtils.conform_opensearch(row)
|
||||
for row in report
|
||||
if BenchmarkUtils.sanity_check(row)
|
||||
]
|
||||
logging.debug(
|
||||
f"upload {len(sanitized)} sane of {len(report)} benchmarks to opensearch"
|
||||
)
|
||||
for single_benchmark in sanitized:
|
||||
logging.debug(f"upload benchmark: {single_benchmark}")
|
||||
response = requests.post(
|
||||
esdocument,
|
||||
json=single_benchmark,
|
||||
auth=(os.environ["ES_USER"], os.environ["ES_PASS"]),
|
||||
)
|
||||
logging.debug(
|
||||
f"Sent to OpenSearch, status: {response.status_code}, result: {response.text}"
|
||||
)
|
||||
response.raise_for_status()
|
||||
|
||||
|
||||
def push_report_to_null(report):
|
||||
|
||||
for row in report:
|
||||
if BenchmarkUtils.sanity_check(row):
|
||||
logging.debug(f"row {row}")
|
||||
conformed = BenchmarkUtils.conform_opensearch(row)
|
||||
logging.debug(f"conformed row {conformed}")
|
||||
|
||||
|
||||
def main():
|
||||
"""Tool for fetching, parsing and uploading benchmark results to OpenSearch / ElasticSearch
|
||||
This tool will
|
||||
|
||||
(1) Open a local tsv benchmark report file
|
||||
(2) Upload to OpenSearch document, via https/JSON
|
||||
"""
|
||||
|
||||
parser = argparse.ArgumentParser(description="CircleCI benchmark scraper.")
|
||||
|
||||
# --tsvfile is the name of the file to read results from
|
||||
# --esdocument is the ElasticSearch document to push these results into
|
||||
#
|
||||
parser.add_argument(
|
||||
"--tsvfile",
|
||||
default="build_tools/circle_api_scraper_input.txt",
|
||||
help="File from which to read tsv report",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--esdocument",
|
||||
help="ElasticSearch/OpenSearch document URL to upload report into",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--upload", choices=["opensearch", "none"], default="opensearch"
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
logging.debug(f"Arguments: {args}")
|
||||
reports = load_report_from_tsv(args.tsvfile)
|
||||
if args.upload == "opensearch":
|
||||
push_report_to_opensearch(reports, args.esdocument)
|
||||
else:
|
||||
push_report_to_null(reports)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
+136
-316
@@ -9,7 +9,6 @@
|
||||
# PLATFORM_LDFLAGS Linker flags
|
||||
# JAVA_LDFLAGS Linker flags for RocksDBJava
|
||||
# JAVA_STATIC_LDFLAGS Linker flags for RocksDBJava static build
|
||||
# JAVAC_ARGS Arguments for javac
|
||||
# PLATFORM_SHARED_EXT Extension for shared libraries
|
||||
# PLATFORM_SHARED_LDFLAGS Flags for building shared library
|
||||
# PLATFORM_SHARED_CFLAGS Flags for compiling objects for shared library
|
||||
@@ -27,7 +26,7 @@
|
||||
# -DLZ4 if the LZ4 library is present
|
||||
# -DZSTD if the ZSTD library is present
|
||||
# -DNUMA if the NUMA library is present
|
||||
# -DMEMKIND if the memkind library is present
|
||||
# -DTBB if the TBB library is present
|
||||
#
|
||||
# Using gflags in rocksdb:
|
||||
# Our project depends on gflags, which requires users to take some extra steps
|
||||
@@ -44,33 +43,24 @@ if test -z "$OUTPUT"; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# we depend on C++20, but should be compatible with newer standards
|
||||
if [ "$ROCKSDB_CXX_STANDARD" ]; then
|
||||
PLATFORM_CXXFLAGS="-std=$ROCKSDB_CXX_STANDARD"
|
||||
else
|
||||
PLATFORM_CXXFLAGS="-std=c++20"
|
||||
fi
|
||||
|
||||
# we depend on C++11
|
||||
PLATFORM_CXXFLAGS="-std=c++11"
|
||||
# we currently depend on POSIX platform
|
||||
COMMON_FLAGS="-DROCKSDB_PLATFORM_POSIX -DROCKSDB_LIB_IO_POSIX"
|
||||
|
||||
# Default to fbcode gcc on Meta internal machines
|
||||
IS_META_HOST="$(hostname | grep -E '(facebook|meta).com|fbinfra.net')"
|
||||
if [ -z "$ROCKSDB_NO_FBCODE" -a "$IS_META_HOST" ]; then
|
||||
if [ -d /mnt/gvfs/third-party ]; then
|
||||
echo "NOTE: Using fbcode build" >&2
|
||||
# Default to fbcode gcc on internal fb machines
|
||||
if [ -z "$ROCKSDB_NO_FBCODE" -a -d /mnt/gvfs/third-party ]; then
|
||||
FBCODE_BUILD="true"
|
||||
# If we're compiling with TSAN or shared lib, we need pic build
|
||||
# If we're compiling with TSAN we need pic build
|
||||
PIC_BUILD=$COMPILE_WITH_TSAN
|
||||
if [ "$LIB_MODE" == "shared" ]; then
|
||||
PIC_BUILD=1
|
||||
if [ -n "$ROCKSDB_FBCODE_BUILD_WITH_481" ]; then
|
||||
# we need this to build with MySQL. Don't use for other purposes.
|
||||
source "$PWD/build_tools/fbcode_config4.8.1.sh"
|
||||
elif [ -n "$ROCKSDB_FBCODE_BUILD_WITH_5xx" ]; then
|
||||
source "$PWD/build_tools/fbcode_config.sh"
|
||||
else
|
||||
source "$PWD/build_tools/fbcode_config_platform007.sh"
|
||||
fi
|
||||
source "$PWD/build_tools/fbcode_config_platform010.sh"
|
||||
else
|
||||
echo "************************************************************************" >&2
|
||||
echo "WARNING: -d /mnt/gvfs/third-party failed; no fbcode build" >&2
|
||||
echo "************************************************************************" >&2
|
||||
fi
|
||||
fi
|
||||
|
||||
# Delete existing output, if it exists
|
||||
@@ -78,9 +68,7 @@ rm -f "$OUTPUT"
|
||||
touch "$OUTPUT"
|
||||
|
||||
if test -z "$CC"; then
|
||||
if [ "$USE_CLANG" -a -x "$(command -v clang)" ]; then
|
||||
CC=clang
|
||||
elif [ -x "$(command -v cc)" ]; then
|
||||
if [ -x "$(command -v cc)" ]; then
|
||||
CC=cc
|
||||
elif [ -x "$(command -v clang)" ]; then
|
||||
CC=clang
|
||||
@@ -90,9 +78,7 @@ if test -z "$CC"; then
|
||||
fi
|
||||
|
||||
if test -z "$CXX"; then
|
||||
if [ "$USE_CLANG" -a -x "$(command -v clang++)" ]; then
|
||||
CXX=clang++
|
||||
elif [ -x "$(command -v g++)" ]; then
|
||||
if [ -x "$(command -v g++)" ]; then
|
||||
CXX=g++
|
||||
elif [ -x "$(command -v clang++)" ]; then
|
||||
CXX=clang++
|
||||
@@ -101,18 +87,6 @@ if test -z "$CXX"; then
|
||||
fi
|
||||
fi
|
||||
|
||||
if test -z "$AR"; then
|
||||
if [ "$USE_CLANG" -a -x "$(command -v llvm-ar)" ]; then
|
||||
AR=llvm-ar
|
||||
elif [ -x "$(command -v gcc-ar)" ]; then
|
||||
AR=gcc-ar
|
||||
elif [ -x "$(command -v llvm-ar)" ]; then
|
||||
AR=llvm-ar
|
||||
else
|
||||
AR=ar
|
||||
fi
|
||||
fi
|
||||
|
||||
# Detect OS
|
||||
if test -z "$TARGET_OS"; then
|
||||
TARGET_OS=`uname -s`
|
||||
@@ -147,24 +121,6 @@ PLATFORM_SHARED_LDFLAGS="-Wl,--no-as-needed -shared -Wl,-soname -Wl,"
|
||||
PLATFORM_SHARED_CFLAGS="-fPIC"
|
||||
PLATFORM_SHARED_VERSIONED=true
|
||||
|
||||
# Prefer lld linker when available on Linux. lld is typically 5-10x faster
|
||||
# than the default ld.bfd for large C++ projects. macOS uses ld64 (or
|
||||
# ld-prime) which is already fast, so we skip lld detection there.
|
||||
# Set ROCKSDB_NO_FAST_LINKER=1 to disable this auto-detection.
|
||||
if [ -z "$ROCKSDB_NO_FAST_LINKER" ] && [ "$TARGET_OS" = "Linux" ]; then
|
||||
if $CXX -fuse-ld=lld -L/usr/local/lib -x c++ - -o /dev/null 2>/dev/null <<EOF
|
||||
int main() { return 0; }
|
||||
EOF
|
||||
then
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -fuse-ld=lld"
|
||||
# Ensure lld can find libraries in /usr/local/lib (lld does not
|
||||
# search there by default, unlike ld.bfd)
|
||||
if [ -d /usr/local/lib ]; then
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -L/usr/local/lib"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# generic port files (working on all platform by #ifdef) go directly in /port
|
||||
GENERIC_PORT_FILES=`cd "$ROCKSDB_ROOT"; find port -name '*.cc' | tr "\n" " "`
|
||||
|
||||
@@ -179,7 +135,7 @@ case "$TARGET_OS" in
|
||||
;;
|
||||
IOS)
|
||||
PLATFORM=IOS
|
||||
COMMON_FLAGS="$COMMON_FLAGS -DOS_MACOSX -DIOS_CROSS_COMPILE "
|
||||
COMMON_FLAGS="$COMMON_FLAGS -DOS_MACOSX -DIOS_CROSS_COMPILE -DROCKSDB_LITE"
|
||||
PLATFORM_SHARED_EXT=dylib
|
||||
PLATFORM_SHARED_LDFLAGS="-dynamiclib -install_name "
|
||||
CROSS_COMPILE=true
|
||||
@@ -193,7 +149,7 @@ case "$TARGET_OS" in
|
||||
else
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -latomic"
|
||||
fi
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -lpthread -lrt -ldl"
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -lpthread -lrt"
|
||||
# PORT_FILES=port/linux/linux_specific.cc
|
||||
;;
|
||||
SunOS)
|
||||
@@ -216,17 +172,6 @@ case "$TARGET_OS" in
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -lpthread"
|
||||
# PORT_FILES=port/freebsd/freebsd_specific.cc
|
||||
;;
|
||||
GNU/kFreeBSD)
|
||||
PLATFORM=OS_GNU_KFREEBSD
|
||||
COMMON_FLAGS="$COMMON_FLAGS -DOS_GNU_KFREEBSD"
|
||||
if [ -z "$USE_CLANG" ]; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS -fno-builtin-memcmp"
|
||||
else
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -latomic"
|
||||
fi
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -lpthread -lrt"
|
||||
# PORT_FILES=port/gnu_kfreebsd/gnu_kfreebsd_specific.cc
|
||||
;;
|
||||
NetBSD)
|
||||
PLATFORM=OS_NETBSD
|
||||
COMMON_FLAGS="$COMMON_FLAGS -fno-builtin-memcmp -D_REENTRANT -DOS_NETBSD"
|
||||
@@ -276,21 +221,15 @@ esac
|
||||
PLATFORM_CXXFLAGS="$PLATFORM_CXXFLAGS ${CXXFLAGS}"
|
||||
JAVA_LDFLAGS="$PLATFORM_LDFLAGS"
|
||||
JAVA_STATIC_LDFLAGS="$PLATFORM_LDFLAGS"
|
||||
JAVAC_ARGS="-source 8"
|
||||
|
||||
if [ "$CROSS_COMPILE" = "true" -o "$FBCODE_BUILD" = "true" ]; then
|
||||
# Cross-compiling; do not try any compilation tests.
|
||||
# Also don't need any compilation tests if compiling on fbcode
|
||||
if [ "$FBCODE_BUILD" = "true" ]; then
|
||||
# Enable backtrace on fbcode since the necessary libraries are present
|
||||
COMMON_FLAGS="$COMMON_FLAGS -DROCKSDB_BACKTRACE"
|
||||
FOLLY_DIR="third-party/folly"
|
||||
fi
|
||||
true
|
||||
else
|
||||
if ! test $ROCKSDB_DISABLE_FALLOCATE; then
|
||||
# Test whether fallocate is available
|
||||
$CXX $PLATFORM_CXXFLAGS -x c++ - -o test.o 2>/dev/null <<EOF
|
||||
$CXX $CFLAGS -x c++ - -o /dev/null 2>/dev/null <<EOF
|
||||
#include <fcntl.h>
|
||||
#include <linux/falloc.h>
|
||||
int main() {
|
||||
@@ -306,7 +245,7 @@ EOF
|
||||
if ! test $ROCKSDB_DISABLE_SNAPPY; then
|
||||
# Test whether Snappy library is installed
|
||||
# http://code.google.com/p/snappy/
|
||||
$CXX $PLATFORM_CXXFLAGS -x c++ - -o test.o 2>/dev/null <<EOF
|
||||
$CXX $CFLAGS -x c++ - -o /dev/null 2>/dev/null <<EOF
|
||||
#include <snappy.h>
|
||||
int main() {}
|
||||
EOF
|
||||
@@ -321,40 +260,30 @@ EOF
|
||||
# Test whether gflags library is installed
|
||||
# http://gflags.github.io/gflags/
|
||||
# check if the namespace is gflags
|
||||
if $CXX $PLATFORM_CXXFLAGS -x c++ - -o test.o 2>/dev/null << EOF
|
||||
$CXX $CFLAGS -x c++ - -o /dev/null 2>/dev/null << EOF
|
||||
#include <gflags/gflags.h>
|
||||
using namespace GFLAGS_NAMESPACE;
|
||||
int main() {}
|
||||
EOF
|
||||
then
|
||||
COMMON_FLAGS="$COMMON_FLAGS -DGFLAGS=1"
|
||||
# Hack: don't link extra gflags assuming it comes with folly
|
||||
[ "$USE_FOLLY" ] || PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -lgflags"
|
||||
# check if namespace is gflags
|
||||
elif $CXX $PLATFORM_CXXFLAGS -x c++ - -o test.o 2>/dev/null << EOF
|
||||
#include <gflags/gflags.h>
|
||||
using namespace gflags;
|
||||
int main() {}
|
||||
EOF
|
||||
then
|
||||
COMMON_FLAGS="$COMMON_FLAGS -DGFLAGS=1 -DGFLAGS_NAMESPACE=gflags"
|
||||
# Hack: don't link extra gflags assuming it comes with folly
|
||||
[ "$USE_FOLLY" ] || PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -lgflags"
|
||||
# check if namespace is google
|
||||
elif $CXX $PLATFORM_CXXFLAGS -x c++ - -o test.o 2>/dev/null << EOF
|
||||
if [ "$?" = 0 ]; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS -DGFLAGS=1"
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -lgflags"
|
||||
else
|
||||
# check if namespace is google
|
||||
$CXX $CFLAGS -x c++ - -o /dev/null 2>/dev/null << EOF
|
||||
#include <gflags/gflags.h>
|
||||
using namespace google;
|
||||
int main() {}
|
||||
EOF
|
||||
then
|
||||
COMMON_FLAGS="$COMMON_FLAGS -DGFLAGS=1 -DGFLAGS_NAMESPACE=google"
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -lgflags"
|
||||
if [ "$?" = 0 ]; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS -DGFLAGS=google"
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -lgflags"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
if ! test $ROCKSDB_DISABLE_ZLIB; then
|
||||
# Test whether zlib library is installed
|
||||
$CXX $PLATFORM_CXXFLAGS $COMMON_FLAGS -x c++ - -o test.o 2>/dev/null <<EOF
|
||||
$CXX $CFLAGS $COMMON_FLAGS -x c++ - -o /dev/null 2>/dev/null <<EOF
|
||||
#include <zlib.h>
|
||||
int main() {}
|
||||
EOF
|
||||
@@ -367,7 +296,7 @@ EOF
|
||||
|
||||
if ! test $ROCKSDB_DISABLE_BZIP; then
|
||||
# Test whether bzip library is installed
|
||||
$CXX $PLATFORM_CXXFLAGS $COMMON_FLAGS -x c++ - -o test.o 2>/dev/null <<EOF
|
||||
$CXX $CFLAGS $COMMON_FLAGS -x c++ - -o /dev/null 2>/dev/null <<EOF
|
||||
#include <bzlib.h>
|
||||
int main() {}
|
||||
EOF
|
||||
@@ -380,7 +309,7 @@ EOF
|
||||
|
||||
if ! test $ROCKSDB_DISABLE_LZ4; then
|
||||
# Test whether lz4 library is installed
|
||||
$CXX $PLATFORM_CXXFLAGS $COMMON_FLAGS -x c++ - -o test.o 2>/dev/null <<EOF
|
||||
$CXX $CFLAGS $COMMON_FLAGS -x c++ - -o /dev/null 2>/dev/null <<EOF
|
||||
#include <lz4.h>
|
||||
#include <lz4hc.h>
|
||||
int main() {}
|
||||
@@ -393,13 +322,9 @@ EOF
|
||||
fi
|
||||
|
||||
if ! test $ROCKSDB_DISABLE_ZSTD; then
|
||||
# Test whether zstd library is installed with minimum version
|
||||
# (Keep in sync with compression.h)
|
||||
$CXX $PLATFORM_CXXFLAGS $COMMON_FLAGS -x c++ - -o /dev/null 2>/dev/null <<EOF
|
||||
# Test whether zstd library is installed
|
||||
$CXX $CFLAGS $COMMON_FLAGS -x c++ - -o /dev/null 2>/dev/null <<EOF
|
||||
#include <zstd.h>
|
||||
#if ZSTD_VERSION_NUMBER < 10400
|
||||
#error "ZSTD support requires version >= 1.4.0 (libzstd-devel)"
|
||||
#endif // ZSTD_VERSION_NUMBER
|
||||
int main() {}
|
||||
EOF
|
||||
if [ "$?" = 0 ]; then
|
||||
@@ -411,7 +336,7 @@ EOF
|
||||
|
||||
if ! test $ROCKSDB_DISABLE_NUMA; then
|
||||
# Test whether numa is available
|
||||
$CXX $PLATFORM_CXXFLAGS -x c++ - -o test.o -lnuma 2>/dev/null <<EOF
|
||||
$CXX $CFLAGS -x c++ - -o /dev/null -lnuma 2>/dev/null <<EOF
|
||||
#include <numa.h>
|
||||
#include <numaif.h>
|
||||
int main() {}
|
||||
@@ -423,9 +348,22 @@ EOF
|
||||
fi
|
||||
fi
|
||||
|
||||
if ! test $ROCKSDB_DISABLE_TBB; then
|
||||
# Test whether tbb is available
|
||||
$CXX $CFLAGS $LDFLAGS -x c++ - -o /dev/null -ltbb 2>/dev/null <<EOF
|
||||
#include <tbb/tbb.h>
|
||||
int main() {}
|
||||
EOF
|
||||
if [ "$?" = 0 ]; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS -DTBB"
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -ltbb"
|
||||
JAVA_LDFLAGS="$JAVA_LDFLAGS -ltbb"
|
||||
fi
|
||||
fi
|
||||
|
||||
if ! test $ROCKSDB_DISABLE_JEMALLOC; then
|
||||
# Test whether jemalloc is available
|
||||
if echo 'int main() {}' | $CXX $PLATFORM_CXXFLAGS $LDFLAGS -x c++ - -o test.o -ljemalloc \
|
||||
if echo 'int main() {}' | $CXX $CFLAGS -x c++ - -o /dev/null -ljemalloc \
|
||||
2>/dev/null; then
|
||||
# This will enable some preprocessor identifiers in the Makefile
|
||||
JEMALLOC=1
|
||||
@@ -434,26 +372,19 @@ EOF
|
||||
WITH_JEMALLOC_FLAG=1
|
||||
# check for JEMALLOC installed with HomeBrew
|
||||
if [ "$PLATFORM" == "OS_MACOSX" ]; then
|
||||
if [ "$TARGET_ARCHITECTURE" = "arm64" ]; then
|
||||
# on M1 Macs, homebrew installs here instead of /usr/local
|
||||
JEMALLOC_PREFIX="/opt/homebrew"
|
||||
else
|
||||
JEMALLOC_PREFIX="/usr/local"
|
||||
fi
|
||||
if hash brew 2>/dev/null && brew ls --versions jemalloc > /dev/null; then
|
||||
JEMALLOC_VER=$(brew ls --versions jemalloc | tail -n 1 | cut -f 2 -d ' ')
|
||||
JEMALLOC_INCLUDE="-I${JEMALLOC_PREFIX}/Cellar/jemalloc/${JEMALLOC_VER}/include"
|
||||
JEMALLOC_LIB="${JEMALLOC_PREFIX}/Cellar/jemalloc/${JEMALLOC_VER}/lib/libjemalloc_pic.a"
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -L${JEMALLOC_PREFIX}/lib $JEMALLOC_LIB"
|
||||
JAVA_LDFLAGS="$JAVA_LDFLAGS -L${JEMALLOC_PREFIX}/lib $JEMALLOC_LIB"
|
||||
JAVA_STATIC_LDFLAGS="$JAVA_STATIC_LDFLAGS -L${JEMALLOC_PREFIX}/lib $JEMALLOC_LIB"
|
||||
JEMALLOC_INCLUDE="-I/usr/local/Cellar/jemalloc/${JEMALLOC_VER}/include"
|
||||
JEMALLOC_LIB="/usr/local/Cellar/jemalloc/${JEMALLOC_VER}/lib/libjemalloc_pic.a"
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS $JEMALLOC_LIB"
|
||||
JAVA_STATIC_LDFLAGS="$JAVA_STATIC_LDFLAGS $JEMALLOC_LIB"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
if ! test $JEMALLOC && ! test $ROCKSDB_DISABLE_TCMALLOC; then
|
||||
# jemalloc is not available. Let's try tcmalloc
|
||||
if echo 'int main() {}' | $CXX $PLATFORM_CXXFLAGS -x c++ - -o test.o \
|
||||
if echo 'int main() {}' | $CXX $CFLAGS -x c++ - -o /dev/null \
|
||||
-ltcmalloc 2>/dev/null; then
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -ltcmalloc"
|
||||
JAVA_LDFLAGS="$JAVA_LDFLAGS -ltcmalloc"
|
||||
@@ -462,7 +393,7 @@ EOF
|
||||
|
||||
if ! test $ROCKSDB_DISABLE_MALLOC_USABLE_SIZE; then
|
||||
# Test whether malloc_usable_size is available
|
||||
$CXX $PLATFORM_CXXFLAGS -x c++ - -o test.o 2>/dev/null <<EOF
|
||||
$CXX $CFLAGS -x c++ - -o /dev/null 2>/dev/null <<EOF
|
||||
#include <malloc.h>
|
||||
int main() {
|
||||
size_t res = malloc_usable_size(0);
|
||||
@@ -475,25 +406,9 @@ EOF
|
||||
fi
|
||||
fi
|
||||
|
||||
if ! test $ROCKSDB_DISABLE_MEMKIND; then
|
||||
# Test whether memkind library is installed
|
||||
$CXX $PLATFORM_CXXFLAGS $LDFLAGS -x c++ - -o test.o -lmemkind 2>/dev/null <<EOF
|
||||
#include <memkind.h>
|
||||
int main() {
|
||||
memkind_malloc(MEMKIND_DAX_KMEM, 1024);
|
||||
return 0;
|
||||
}
|
||||
EOF
|
||||
if [ "$?" = 0 ]; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS -DMEMKIND"
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -lmemkind"
|
||||
JAVA_LDFLAGS="$JAVA_LDFLAGS -lmemkind"
|
||||
fi
|
||||
fi
|
||||
|
||||
if ! test $ROCKSDB_DISABLE_PTHREAD_MUTEX_ADAPTIVE_NP; then
|
||||
# Test whether PTHREAD_MUTEX_ADAPTIVE_NP mutex type is available
|
||||
$CXX $PLATFORM_CXXFLAGS -x c++ - -o test.o 2>/dev/null <<EOF
|
||||
$CXX $CFLAGS -x c++ - -o /dev/null 2>/dev/null <<EOF
|
||||
#include <pthread.h>
|
||||
int main() {
|
||||
int x = PTHREAD_MUTEX_ADAPTIVE_NP;
|
||||
@@ -508,7 +423,7 @@ EOF
|
||||
|
||||
if ! test $ROCKSDB_DISABLE_BACKTRACE; then
|
||||
# Test whether backtrace is available
|
||||
$CXX $PLATFORM_CXXFLAGS -x c++ - -o test.o 2>/dev/null <<EOF
|
||||
$CXX $CFLAGS -x c++ - -o /dev/null 2>/dev/null <<EOF
|
||||
#include <execinfo.h>
|
||||
int main() {
|
||||
void* frames[1];
|
||||
@@ -520,7 +435,7 @@ EOF
|
||||
COMMON_FLAGS="$COMMON_FLAGS -DROCKSDB_BACKTRACE"
|
||||
else
|
||||
# Test whether execinfo library is installed
|
||||
$CXX $PLATFORM_CXXFLAGS -lexecinfo -x c++ - -o test.o 2>/dev/null <<EOF
|
||||
$CXX $CFLAGS -lexecinfo -x c++ - -o /dev/null 2>/dev/null <<EOF
|
||||
#include <execinfo.h>
|
||||
int main() {
|
||||
void* frames[1];
|
||||
@@ -537,7 +452,7 @@ EOF
|
||||
|
||||
if ! test $ROCKSDB_DISABLE_PG; then
|
||||
# Test if -pg is supported
|
||||
$CXX $PLATFORM_CXXFLAGS -pg -x c++ - -o test.o 2>/dev/null <<EOF
|
||||
$CXX $CFLAGS -pg -x c++ - -o /dev/null 2>/dev/null <<EOF
|
||||
int main() {
|
||||
return 0;
|
||||
}
|
||||
@@ -549,7 +464,7 @@ EOF
|
||||
|
||||
if ! test $ROCKSDB_DISABLE_SYNC_FILE_RANGE; then
|
||||
# Test whether sync_file_range is supported for compatibility with an old glibc
|
||||
$CXX $PLATFORM_CXXFLAGS -x c++ - -o test.o 2>/dev/null <<EOF
|
||||
$CXX $CFLAGS -x c++ - -o /dev/null 2>/dev/null <<EOF
|
||||
#include <fcntl.h>
|
||||
int main() {
|
||||
int fd = open("/dev/null", 0);
|
||||
@@ -563,7 +478,7 @@ EOF
|
||||
|
||||
if ! test $ROCKSDB_DISABLE_SCHED_GETCPU; then
|
||||
# Test whether sched_getcpu is supported
|
||||
$CXX $PLATFORM_CXXFLAGS -x c++ - -o test.o 2>/dev/null <<EOF
|
||||
$CXX $CFLAGS -x c++ - -o /dev/null 2>/dev/null <<EOF
|
||||
#include <sched.h>
|
||||
int main() {
|
||||
int cpuid = sched_getcpu();
|
||||
@@ -575,23 +490,9 @@ EOF
|
||||
fi
|
||||
fi
|
||||
|
||||
if ! test $ROCKSDB_DISABLE_AUXV_GETAUXVAL; then
|
||||
# Test whether getauxval is supported
|
||||
$CXX $PLATFORM_CXXFLAGS -x c++ - -o test.o 2>/dev/null <<EOF
|
||||
#include <sys/auxv.h>
|
||||
int main() {
|
||||
uint64_t auxv = getauxval(AT_HWCAP);
|
||||
(void)auxv;
|
||||
}
|
||||
EOF
|
||||
if [ "$?" = 0 ]; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS -DROCKSDB_AUXV_GETAUXVAL_PRESENT"
|
||||
fi
|
||||
fi
|
||||
|
||||
if ! test $ROCKSDB_DISABLE_ALIGNED_NEW; then
|
||||
# Test whether c++17 aligned-new is supported
|
||||
$CXX $PLATFORM_CXXFLAGS -faligned-new -x c++ - -o test.o 2>/dev/null <<EOF
|
||||
$CXX $PLATFORM_CXXFLAGS -faligned-new -x c++ - -o /dev/null 2>/dev/null <<EOF
|
||||
struct alignas(1024) t {int a;};
|
||||
int main() {}
|
||||
EOF
|
||||
@@ -599,52 +500,13 @@ EOF
|
||||
PLATFORM_CXXFLAGS="$PLATFORM_CXXFLAGS -faligned-new -DHAVE_ALIGNED_NEW"
|
||||
fi
|
||||
fi
|
||||
if ! test $ROCKSDB_DISABLE_BENCHMARK; then
|
||||
# Test whether google benchmark is available
|
||||
$CXX $PLATFORM_CXXFLAGS -x c++ - -o /dev/null -lbenchmark -lpthread 2>/dev/null <<EOF
|
||||
#include <benchmark/benchmark.h>
|
||||
int main() {}
|
||||
EOF
|
||||
if [ "$?" = 0 ]; then
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -lbenchmark"
|
||||
fi
|
||||
fi
|
||||
if test $USE_FOLLY || test $USE_FOLLY_LITE; then
|
||||
# Test whether libfolly library is installed
|
||||
$CXX $PLATFORM_CXXFLAGS $COMMON_FLAGS -x c++ - -o /dev/null 2>/dev/null <<EOF
|
||||
#include <folly/synchronization/DistributedMutex.h>
|
||||
int main() {}
|
||||
EOF
|
||||
if [ "$?" != 0 ]; then
|
||||
FOLLY_DIR="./third-party/folly"
|
||||
fi
|
||||
fi
|
||||
|
||||
if test -z "$ROCKSDB_USE_IO_URING"; then
|
||||
ROCKSDB_USE_IO_URING=1
|
||||
fi
|
||||
if [ "$ROCKSDB_USE_IO_URING" -ne 0 -a "$PLATFORM" = OS_LINUX ]; then
|
||||
# check for liburing
|
||||
$CXX $PLATFORM_CXXFLAGS -x c++ - -luring -o test.o 2>/dev/null <<EOF
|
||||
#include <liburing.h>
|
||||
int main() {
|
||||
struct io_uring ring;
|
||||
io_uring_queue_init(1, &ring, 0);
|
||||
return 0;
|
||||
}
|
||||
EOF
|
||||
if [ "$?" = 0 ]; then
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -luring"
|
||||
COMMON_FLAGS="$COMMON_FLAGS -DROCKSDB_IOURING_PRESENT"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# TODO(tec): Fix -Wshorten-64-to-32 errors on FreeBSD and enable the warning.
|
||||
# -Wshorten-64-to-32 breaks compilation on FreeBSD aarch64 and i386
|
||||
if ! { [ "$TARGET_OS" = FreeBSD -o "$TARGET_OS" = OpenBSD ] && [ "$TARGET_ARCHITECTURE" = arm64 -o "$TARGET_ARCHITECTURE" = i386 ]; }; then
|
||||
# -Wshorten-64-to-32 breaks compilation on FreeBSD i386
|
||||
if ! [ "$TARGET_OS" = FreeBSD -a "$TARGET_ARCHITECTURE" = i386 ]; then
|
||||
# Test whether -Wshorten-64-to-32 is available
|
||||
$CXX $PLATFORM_CXXFLAGS -x c++ - -o test.o -Wshorten-64-to-32 2>/dev/null <<EOF
|
||||
$CXX $CFLAGS -x c++ - -o /dev/null -Wshorten-64-to-32 2>/dev/null <<EOF
|
||||
int main() {}
|
||||
EOF
|
||||
if [ "$?" = 0 ]; then
|
||||
@@ -652,94 +514,94 @@ EOF
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$PORTABLE" == "" ] || [ "$PORTABLE" == 0 ]; then
|
||||
# shall we use HDFS?
|
||||
|
||||
if test "$USE_HDFS"; then
|
||||
if test -z "$JAVA_HOME"; then
|
||||
echo "JAVA_HOME has to be set for HDFS usage." >&2
|
||||
exit 1
|
||||
fi
|
||||
HDFS_CCFLAGS="$HDFS_CCFLAGS -I$JAVA_HOME/include -I$JAVA_HOME/include/linux -DUSE_HDFS -I$HADOOP_HOME/include"
|
||||
HDFS_LDFLAGS="$HDFS_LDFLAGS -lhdfs -L$JAVA_HOME/jre/lib/amd64 -L$HADOOP_HOME/lib/native"
|
||||
HDFS_LDFLAGS="$HDFS_LDFLAGS -L$JAVA_HOME/jre/lib/amd64/server -L$GLIBC_RUNTIME_PATH/lib"
|
||||
HDFS_LDFLAGS="$HDFS_LDFLAGS -ldl -lverify -ljava -ljvm"
|
||||
COMMON_FLAGS="$COMMON_FLAGS $HDFS_CCFLAGS"
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS $HDFS_LDFLAGS"
|
||||
JAVA_LDFLAGS="$JAVA_LDFLAGS $HDFS_LDFLAGS"
|
||||
fi
|
||||
|
||||
if test -z "$PORTABLE"; then
|
||||
if test -n "`echo $TARGET_ARCHITECTURE | grep ^ppc64`"; then
|
||||
# Tune for this POWER processor, treating '+' models as base models
|
||||
POWER=`LD_SHOW_AUXV=1 /bin/true | grep AT_PLATFORM | grep -E -o power[0-9]+`
|
||||
COMMON_FLAGS="$COMMON_FLAGS -mcpu=$POWER -mtune=$POWER "
|
||||
elif test -n "`echo $TARGET_ARCHITECTURE | grep ^s390x`"; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS -march=z10 "
|
||||
elif test -n "`echo $TARGET_ARCHITECTURE | grep -e^arm -e^aarch64`"; then
|
||||
# TODO: Handle this with approprite options.
|
||||
COMMON_FLAGS="$COMMON_FLAGS"
|
||||
elif test -n "`echo $TARGET_ARCHITECTURE | grep ^aarch64`"; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS"
|
||||
elif test -n "`echo $TARGET_ARCHITECTURE | grep ^s390x`"; then
|
||||
if echo 'int main() {}' | $CXX $PLATFORM_CXXFLAGS -x c++ \
|
||||
-march=native - -o /dev/null 2>/dev/null; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS -march=native "
|
||||
else
|
||||
COMMON_FLAGS="$COMMON_FLAGS -march=z196 "
|
||||
fi
|
||||
COMMON_FLAGS="$COMMON_FLAGS"
|
||||
elif test -n "`echo $TARGET_ARCHITECTURE | grep ^riscv64`"; then
|
||||
RISC_ISA=$(cat /proc/cpuinfo | grep -E '^isa\s*:' | head -1 | cut --delimiter=: -f 2 | cut -b 2-)
|
||||
if [ -n "${RISCV_ISA}" ]; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS -march=${RISC_ISA}"
|
||||
fi
|
||||
elif [ "$TARGET_OS" == "IOS" ]; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS"
|
||||
else
|
||||
elif [ "$TARGET_OS" != "AIX" ] && [ "$TARGET_OS" != "SunOS" ]; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS -march=native "
|
||||
elif test "$USE_SSE"; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS -msse4.2 -mpclmul"
|
||||
fi
|
||||
else
|
||||
# PORTABLE specified
|
||||
if [ "$PORTABLE" == 1 ]; then
|
||||
if test -n "`echo $TARGET_ARCHITECTURE | grep ^s390x`"; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS -march=z196 "
|
||||
elif test -n "`echo $TARGET_ARCHITECTURE | grep ^riscv64`"; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS -march=rv64gc"
|
||||
elif test "$USE_SSE"; then
|
||||
# USE_SSE is DEPRECATED
|
||||
# This is a rough approximation of the old USE_SSE behavior
|
||||
COMMON_FLAGS="$COMMON_FLAGS -march=haswell"
|
||||
fi
|
||||
# Other than those cases, not setting -march= here.
|
||||
else
|
||||
# Assume PORTABLE is a minimum assumed cpu type, e.g. PORTABLE=haswell
|
||||
COMMON_FLAGS="$COMMON_FLAGS -march=${PORTABLE}"
|
||||
fi
|
||||
|
||||
if [[ "${PLATFORM}" == "OS_MACOSX" ]]; then
|
||||
# For portability compile for macOS 10.14 (2018) or newer
|
||||
COMMON_FLAGS="$COMMON_FLAGS -mmacosx-version-min=10.14"
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -mmacosx-version-min=10.14"
|
||||
# -mmacosx-version-min must come first here.
|
||||
PLATFORM_SHARED_LDFLAGS="-mmacosx-version-min=10.14 $PLATFORM_SHARED_LDFLAGS"
|
||||
PLATFORM_CMAKE_FLAGS="-DCMAKE_OSX_DEPLOYMENT_TARGET=10.14"
|
||||
JAVA_STATIC_DEPS_COMMON_FLAGS="-mmacosx-version-min=10.14"
|
||||
JAVA_STATIC_DEPS_LDFLAGS="$JAVA_STATIC_DEPS_COMMON_FLAGS"
|
||||
JAVA_STATIC_DEPS_CCFLAGS="$JAVA_STATIC_DEPS_COMMON_FLAGS"
|
||||
JAVA_STATIC_DEPS_CXXFLAGS="$JAVA_STATIC_DEPS_COMMON_FLAGS"
|
||||
fi
|
||||
elif test "$USE_SSE"; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS -msse4.2 -mpclmul"
|
||||
fi
|
||||
|
||||
if test -n "`echo $TARGET_ARCHITECTURE | grep ^ppc64`"; then
|
||||
# check for GNU libc on ppc64
|
||||
$CXX -x c++ - -o /dev/null 2>/dev/null <<EOF
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <gnu/libc-version.h>
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
printf("GNU libc version: %s\n", gnu_get_libc_version());
|
||||
return 0;
|
||||
}
|
||||
EOF
|
||||
if [ "$?" != 0 ]; then
|
||||
PPC_LIBC_IS_GNU=0
|
||||
fi
|
||||
fi
|
||||
|
||||
$CXX $PLATFORM_CXXFLAGS $COMMON_FLAGS -x c++ - -o test.o 2>/dev/null <<EOF
|
||||
$CXX $PLATFORM_CXXFLAGS $COMMON_FLAGS -x c++ - -o /dev/null 2>/dev/null <<EOF
|
||||
#include <cstdint>
|
||||
#include <nmmintrin.h>
|
||||
int main() {
|
||||
uint64_t a = 0xffffFFFFffffFFFF;
|
||||
__uint128_t b = __uint128_t(a) * a;
|
||||
a = static_cast<uint64_t>(b >> 64);
|
||||
(void)a;
|
||||
volatile uint32_t x = _mm_crc32_u32(0, 0);
|
||||
(void)x;
|
||||
}
|
||||
EOF
|
||||
if [ "$?" = 0 ]; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS -DHAVE_UINT128_EXTENSION"
|
||||
COMMON_FLAGS="$COMMON_FLAGS -DHAVE_SSE42"
|
||||
elif test "$USE_SSE"; then
|
||||
echo "warning: USE_SSE specified but compiler could not use SSE intrinsics, disabling" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
$CXX $PLATFORM_CXXFLAGS $COMMON_FLAGS -x c++ - -o /dev/null 2>/dev/null <<EOF
|
||||
#include <cstdint>
|
||||
#include <wmmintrin.h>
|
||||
int main() {
|
||||
const auto a = _mm_set_epi64x(0, 0);
|
||||
const auto b = _mm_set_epi64x(0, 0);
|
||||
const auto c = _mm_clmulepi64_si128(a, b, 0x00);
|
||||
auto d = _mm_cvtsi128_si64(c);
|
||||
(void)d;
|
||||
}
|
||||
EOF
|
||||
if [ "$?" = 0 ]; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS -DHAVE_PCLMUL"
|
||||
elif test "$USE_SSE"; then
|
||||
echo "warning: USE_SSE specified but compiler could not use PCLMUL intrinsics, disabling" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# iOS doesn't support thread-local storage, but this check would erroneously
|
||||
# succeed because the cross-compiler flags are added by the Makefile, not this
|
||||
# script.
|
||||
if [ "$PLATFORM" != IOS ]; then
|
||||
$CXX $COMMON_FLAGS -x c++ - -o /dev/null 2>/dev/null <<EOF
|
||||
#if defined(_MSC_VER) && !defined(__thread)
|
||||
#define __thread __declspec(thread)
|
||||
#endif
|
||||
int main() {
|
||||
static __thread int tls;
|
||||
(void)tls;
|
||||
}
|
||||
EOF
|
||||
if [ "$?" = 0 ]; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS -DROCKSDB_SUPPORT_THREAD_LOCAL"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$FBCODE_BUILD" != "true" -a "$PLATFORM" = OS_LINUX ]; then
|
||||
@@ -747,7 +609,7 @@ if [ "$FBCODE_BUILD" != "true" -a "$PLATFORM" = OS_LINUX ]; then
|
||||
void dummy_func() {}
|
||||
EOF
|
||||
if [ "$?" = 0 ]; then
|
||||
$CXX $COMMON_FLAGS $PLATFORM_SHARED_LDFLAGS test_dl.o -o test.o 2>/dev/null
|
||||
$CXX $COMMON_FLAGS $PLATFORM_SHARED_LDFLAGS test_dl.o -o /dev/null 2>/dev/null
|
||||
if [ "$?" = 0 ]; then
|
||||
EXEC_LDFLAGS+="-ldl"
|
||||
rm -f test_dl.o
|
||||
@@ -755,33 +617,6 @@ EOF
|
||||
fi
|
||||
fi
|
||||
|
||||
# check for F_FULLFSYNC
|
||||
$CXX $PLATFORM_CXXFALGS -x c++ - -o test.o 2>/dev/null <<EOF
|
||||
#include <fcntl.h>
|
||||
int main() {
|
||||
fcntl(0, F_FULLFSYNC);
|
||||
return 0;
|
||||
}
|
||||
EOF
|
||||
if [ "$?" = 0 ]; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS -DHAVE_FULLFSYNC"
|
||||
fi
|
||||
|
||||
rm -f test.o test_dl.o
|
||||
|
||||
# Get the path for the folly installation dir
|
||||
if [ "$USE_FOLLY" ]; then
|
||||
if [ "$FOLLY_DIR" ]; then
|
||||
FOLLY_PATH=`cd $FOLLY_DIR && $PYTHON build/fbcode_builder/getdeps.py show-inst-dir folly`
|
||||
fi
|
||||
fi
|
||||
if [ "$USE_FOLLY_LITE" ]; then
|
||||
if [ "$FOLLY_DIR" ]; then
|
||||
BOOST_SOURCE_PATH=`cd $FOLLY_DIR && $PYTHON build/fbcode_builder/getdeps.py show-source-dir boost`
|
||||
FMT_SOURCE_PATH=`cd $FOLLY_DIR && $PYTHON build/fbcode_builder/getdeps.py show-source-dir fmt`
|
||||
fi
|
||||
fi
|
||||
|
||||
PLATFORM_CCFLAGS="$PLATFORM_CCFLAGS $COMMON_FLAGS"
|
||||
PLATFORM_CXXFLAGS="$PLATFORM_CXXFLAGS $COMMON_FLAGS"
|
||||
|
||||
@@ -793,16 +628,10 @@ ROCKSDB_PATCH=`build_tools/version.sh patch`
|
||||
|
||||
echo "CC=$CC" >> "$OUTPUT"
|
||||
echo "CXX=$CXX" >> "$OUTPUT"
|
||||
echo "AR=$AR" >> "$OUTPUT"
|
||||
echo "PLATFORM=$PLATFORM" >> "$OUTPUT"
|
||||
echo "PLATFORM_LDFLAGS=$PLATFORM_LDFLAGS" >> "$OUTPUT"
|
||||
echo "PLATFORM_CMAKE_FLAGS=$PLATFORM_CMAKE_FLAGS" >> "$OUTPUT"
|
||||
echo "JAVA_LDFLAGS=$JAVA_LDFLAGS" >> "$OUTPUT"
|
||||
echo "JAVA_STATIC_LDFLAGS=$JAVA_STATIC_LDFLAGS" >> "$OUTPUT"
|
||||
echo "JAVA_STATIC_DEPS_CCFLAGS=$JAVA_STATIC_DEPS_CCFLAGS" >> "$OUTPUT"
|
||||
echo "JAVA_STATIC_DEPS_CXXFLAGS=$JAVA_STATIC_DEPS_CXXFLAGS" >> "$OUTPUT"
|
||||
echo "JAVA_STATIC_DEPS_LDFLAGS=$JAVA_STATIC_DEPS_LDFLAGS" >> "$OUTPUT"
|
||||
echo "JAVAC_ARGS=$JAVAC_ARGS" >> "$OUTPUT"
|
||||
echo "VALGRIND_VER=$VALGRIND_VER" >> "$OUTPUT"
|
||||
echo "PLATFORM_CCFLAGS=$PLATFORM_CCFLAGS" >> "$OUTPUT"
|
||||
echo "PLATFORM_CXXFLAGS=$PLATFORM_CXXFLAGS" >> "$OUTPUT"
|
||||
@@ -821,10 +650,6 @@ echo "CLANG_ANALYZER=$CLANG_ANALYZER" >> "$OUTPUT"
|
||||
echo "PROFILING_FLAGS=$PROFILING_FLAGS" >> "$OUTPUT"
|
||||
echo "FIND=$FIND" >> "$OUTPUT"
|
||||
echo "WATCH=$WATCH" >> "$OUTPUT"
|
||||
echo "FOLLY_PATH=$FOLLY_PATH" >> "$OUTPUT"
|
||||
echo "BOOST_SOURCE_PATH=$BOOST_SOURCE_PATH" >> "$OUTPUT"
|
||||
echo "FMT_SOURCE_PATH=$FMT_SOURCE_PATH" >> "$OUTPUT"
|
||||
|
||||
# This will enable some related identifiers for the preprocessor
|
||||
if test -n "$JEMALLOC"; then
|
||||
echo "JEMALLOC=1" >> "$OUTPUT"
|
||||
@@ -835,9 +660,4 @@ fi
|
||||
if test -n "$WITH_JEMALLOC_FLAG"; then
|
||||
echo "WITH_JEMALLOC_FLAG=$WITH_JEMALLOC_FLAG" >> "$OUTPUT"
|
||||
fi
|
||||
if test -n "$USE_FOLLY"; then
|
||||
echo "USE_FOLLY=$USE_FOLLY" >> "$OUTPUT"
|
||||
fi
|
||||
if test -n "$PPC_LIBC_IS_GNU"; then
|
||||
echo "PPC_LIBC_IS_GNU=$PPC_LIBC_IS_GNU" >> "$OUTPUT"
|
||||
fi
|
||||
echo "LUA_PATH=$LUA_PATH" >> "$OUTPUT"
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
# This source code is licensed under both the GPLv2 (found in the
|
||||
# COPYING file in the root directory) and Apache 2.0 License
|
||||
# (found in the LICENSE.Apache file in the root directory).
|
||||
#
|
||||
# Check for some simple mistakes in public headers (on the command line)
|
||||
# that should prevent commit or push
|
||||
|
||||
BAD=""
|
||||
|
||||
# Look for potential for ODR violations caused by public headers depending on
|
||||
# build parameters that could vary between RocksDB build and application build.
|
||||
# * Cases like ROCKSDB_NAMESPACE, and ROCKSDB_ASSERT_STATUS_CHECKED are
|
||||
# intentional, hard to avoid. (We expect definitions to change and the user
|
||||
# should also.)
|
||||
# * Cases like _WIN32, OS_WIN, and __cplusplus are essentially ODR-safe.
|
||||
# * Cases like
|
||||
# #ifdef BLAH // ODR-SAFE
|
||||
# #undef BLAH
|
||||
# #endif
|
||||
# that should not cause ODR violations can be exempted with the ODR-SAFE
|
||||
# marker recognized here.
|
||||
|
||||
grep -nHE '^#if' -- "$@" | grep -vE 'ROCKSDB_NAMESPACE|ROCKSDB_ASSERT_STATUS_CHECKED|_WIN32|OS_WIN|ODR-SAFE|__cplusplus|ROCKSDB_DLL|ROCKSDB_LIBRARY_EXPORTS'
|
||||
if [ "$?" != "1" ]; then
|
||||
echo "^^^^^ #if in public API could cause an ODR violation."
|
||||
echo " Add // ODR-SAFE if verified safe."
|
||||
BAD=1
|
||||
fi
|
||||
|
||||
if [ "$BAD" ]; then
|
||||
exit 1
|
||||
fi
|
||||
@@ -1,48 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
|
||||
#
|
||||
# Check for some simple mistakes that should prevent commit or push
|
||||
|
||||
BAD=""
|
||||
|
||||
git grep -n 'namespace rocksdb' -- '*.[ch]*'
|
||||
if [ "$?" != "1" ]; then
|
||||
echo "^^^^^ Do not hardcode namespace rocksdb. Use ROCKSDB_NAMESPACE"
|
||||
BAD=1
|
||||
fi
|
||||
|
||||
git grep -n -i 'nocommit' -- ':!build_tools/check-sources.sh'
|
||||
if [ "$?" != "1" ]; then
|
||||
echo "^^^^^ Code was not intended to be committed"
|
||||
BAD=1
|
||||
fi
|
||||
|
||||
git grep -n 'include <rocksdb/' -- ':!build_tools/check-sources.sh'
|
||||
if [ "$?" != "1" ]; then
|
||||
echo '^^^^^ Use double-quotes as in #include "rocksdb/something.h"'
|
||||
BAD=1
|
||||
fi
|
||||
|
||||
git grep -n 'include "include/rocksdb/' -- ':!build_tools/check-sources.sh'
|
||||
if [ "$?" != "1" ]; then
|
||||
echo '^^^^^ Use #include "rocksdb/something.h" instead of #include "include/rocksdb/something.h"'
|
||||
BAD=1
|
||||
fi
|
||||
|
||||
git grep -n 'using namespace' -- ':!build_tools' ':!docs' \
|
||||
':!third-party/folly/folly/lang/Align.h' \
|
||||
':!third-party/gtest-1.8.1/fused-src/gtest/gtest.h'
|
||||
if [ "$?" != "1" ]; then
|
||||
echo '^^^^ Do not use "using namespace"'
|
||||
BAD=1
|
||||
fi
|
||||
|
||||
LC_ALL=C git grep -n $'[\x80-\xff]' -- ':!docs' ':!*.md' ':!.github'
|
||||
if [ "$?" != "1" ]; then
|
||||
echo '^^^^ Use only ASCII characters in source files'
|
||||
BAD=1
|
||||
fi
|
||||
|
||||
if [ "$BAD" ]; then
|
||||
exit 1
|
||||
fi
|
||||
@@ -1,42 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
|
||||
#
|
||||
# Validate GitHub Actions workflow YAML before it reaches CI runtime.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
if ! command -v ruby >/dev/null 2>&1; then
|
||||
echo "ruby is required to validate GitHub Actions workflow YAML"
|
||||
echo "On CentOS Stream: sudo dnf install ruby rubygems rubygem-psych"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! ruby -e 'require "psych"' 2>/dev/null; then
|
||||
echo "ruby is installed but cannot load required library 'psych'"
|
||||
echo "On CentOS Stream: sudo dnf install rubygems rubygem-psych"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
ruby <<'RUBY'
|
||||
require "psych"
|
||||
|
||||
bad = false
|
||||
workflow_files = Dir[".github/workflows/*.{yml,yaml}"].sort
|
||||
|
||||
if workflow_files.empty?
|
||||
warn "No workflow YAML files found under .github/workflows"
|
||||
exit 1
|
||||
end
|
||||
|
||||
workflow_files.each do |path|
|
||||
begin
|
||||
Psych.parse_file(path)
|
||||
puts "OK #{path}"
|
||||
rescue Psych::Exception => e
|
||||
warn "Invalid YAML in #{path}: #{e.message}"
|
||||
bad = true
|
||||
end
|
||||
end
|
||||
|
||||
exit(bad ? 1 : 0)
|
||||
RUBY
|
||||
@@ -1,231 +0,0 @@
|
||||
#!/bin/bash
|
||||
# Output test progress in JSON format for machine parsing
|
||||
# Usage: build_tools/check_progress.sh
|
||||
|
||||
LOG_FILE="LOG"
|
||||
T_DIR="t"
|
||||
SRC_MK="src.mk"
|
||||
|
||||
# Maximum lines of test output to include per failed test
|
||||
MAX_OUTPUT_LINES=50
|
||||
|
||||
# Helper to escape string for JSON (handles newlines, quotes, backslashes, tabs)
|
||||
json_escape() {
|
||||
local str="$1"
|
||||
# Use python for reliable JSON escaping if available, otherwise use sed
|
||||
if command -v python3 &>/dev/null; then
|
||||
printf '%s' "$str" | python3 -c 'import json,sys; print(json.dumps(sys.stdin.read())[1:-1], end="")'
|
||||
else
|
||||
printf '%s' "$str" | sed 's/\\/\\\\/g; s/"/\\"/g; s/\t/\\t/g; s/\r/\\r/g' | awk '{printf "%s\\n", $0}' | sed 's/\\n$//'
|
||||
fi
|
||||
}
|
||||
|
||||
# Helper to output JSON and exit
|
||||
output_json() {
|
||||
local status="$1"
|
||||
local completed="${2:-0}"
|
||||
local total="${3:-0}"
|
||||
local failed="${4:-0}"
|
||||
local percent="${5:-0}"
|
||||
local eta="${6:-0}"
|
||||
local avg_time="${7:-0}"
|
||||
local last_item="${8:-}"
|
||||
local phase="${9:-}"
|
||||
local failed_tests="${10:-}"
|
||||
|
||||
# Build JSON output
|
||||
local json="{\"status\":\"$status\""
|
||||
|
||||
if [[ -n "$phase" ]]; then
|
||||
json="$json,\"phase\":\"$phase\""
|
||||
fi
|
||||
|
||||
json="$json,\"completed\":$completed,\"total\":$total,\"failed\":$failed,\"percent\":$percent"
|
||||
json="$json,\"eta_seconds\":$eta,\"avg_time\":\"$avg_time\",\"last_item\":\"$(json_escape "$last_item")\""
|
||||
|
||||
if [[ -n "$failed_tests" ]]; then
|
||||
json="$json,\"failed_tests\":[$failed_tests]"
|
||||
fi
|
||||
|
||||
json="$json}"
|
||||
echo "$json"
|
||||
}
|
||||
|
||||
# Get failed test info with log output
|
||||
get_failed_tests_json() {
|
||||
local log_file="$1"
|
||||
local t_dir="$2"
|
||||
local max_failures=10
|
||||
local count=0
|
||||
local first=true
|
||||
|
||||
# Get failed tests from LOG file
|
||||
while IFS=$'\t' read -r seq host starttime runtime send recv exitval signal cmd; do
|
||||
# Skip header line
|
||||
[[ "$seq" == "Seq" ]] && continue
|
||||
|
||||
# Check if failed (exitval != 0 or signal != 0)
|
||||
if [[ "$exitval" != "0" || "$signal" != "0" ]]; then
|
||||
# Extract test name from command
|
||||
test_name=$(echo "$cmd" | sed 's,.*/run-,,;s, .*,,')
|
||||
|
||||
# Get log file path
|
||||
log_path="$t_dir/log-run-$test_name"
|
||||
|
||||
# Read test output (last N lines)
|
||||
if [[ -f "$log_path" ]]; then
|
||||
output=$(tail -n "$MAX_OUTPUT_LINES" "$log_path" 2>/dev/null)
|
||||
else
|
||||
output="(log file not found: $log_path)"
|
||||
fi
|
||||
|
||||
# Escape output for JSON
|
||||
escaped_output=$(json_escape "$output")
|
||||
|
||||
# Build JSON object for this failure
|
||||
if [[ "$first" == "true" ]]; then
|
||||
first=false
|
||||
else
|
||||
printf ","
|
||||
fi
|
||||
printf '{"test":"%s","exit_code":%d,"signal":%d,"output":"%s"}' \
|
||||
"$test_name" "$exitval" "$signal" "$escaped_output"
|
||||
|
||||
((count++))
|
||||
if [[ $count -ge $max_failures ]]; then
|
||||
break
|
||||
fi
|
||||
fi
|
||||
done < "$log_file"
|
||||
}
|
||||
|
||||
# Check if tests are running (LOG file exists)
|
||||
if [[ -f "$LOG_FILE" ]]; then
|
||||
# Count total tests from t/run-* files
|
||||
if [[ -d "$T_DIR" ]]; then
|
||||
total=$(find "$T_DIR" -name 'run-*' -type f 2>/dev/null | wc -l)
|
||||
else
|
||||
total=0
|
||||
fi
|
||||
|
||||
# If no parallel tests generated yet
|
||||
if [[ "$total" -eq 0 ]]; then
|
||||
output_json "running" 0 0 0 0 0 "0" "" "generating"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Parse LOG file (skip header line)
|
||||
# LOG format: Seq Host Starttime JobRuntime Send Receive Exitval Signal Command
|
||||
completed=$(tail -n +2 "$LOG_FILE" 2>/dev/null | wc -l)
|
||||
|
||||
# Count failures
|
||||
failed=$(awk -F'\t' 'NR>1 && ($7 != 0 || $8 != 0) {count++} END {print count+0}' "$LOG_FILE" 2>/dev/null)
|
||||
|
||||
# Get failed tests JSON with output (only if there are failures)
|
||||
if [[ "$failed" -gt 0 ]]; then
|
||||
failed_tests=$(get_failed_tests_json "$LOG_FILE" "$T_DIR")
|
||||
else
|
||||
failed_tests=""
|
||||
fi
|
||||
|
||||
# Calculate percentage
|
||||
if [[ "$total" -gt 0 ]]; then
|
||||
percent=$((completed * 100 / total))
|
||||
else
|
||||
percent=0
|
||||
fi
|
||||
|
||||
# Get last completed test name (extract from command column)
|
||||
last_test=$(tail -1 "$LOG_FILE" 2>/dev/null | awk -F'\t' '{print $9}' | sed 's,.*/run-,,;s, .*,,;s,^./,,')
|
||||
|
||||
# Calculate ETA based on average time
|
||||
if [[ "$completed" -gt 0 ]]; then
|
||||
avg_time=$(awk -F'\t' 'NR>1 {sum+=$4; count++} END {if(count>0) printf "%.1f", sum/count; else print "0"}' "$LOG_FILE")
|
||||
remaining=$((total - completed))
|
||||
eta=$(awk "BEGIN {printf \"%.0f\", $avg_time * $remaining}")
|
||||
else
|
||||
avg_time="0"
|
||||
eta="0"
|
||||
fi
|
||||
|
||||
# Determine status
|
||||
if [[ "$completed" -ge "$total" ]]; then
|
||||
status="completed"
|
||||
elif [[ "$completed" -gt 0 ]]; then
|
||||
status="running"
|
||||
else
|
||||
status="starting"
|
||||
fi
|
||||
|
||||
output_json "$status" "$completed" "$total" "$failed" "$percent" "$eta" "$avg_time" "$last_test" "testing" "$failed_tests"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# No LOG file - check if we're in compilation/linking phase
|
||||
# Count expected source files from src.mk
|
||||
if [[ -f "$SRC_MK" ]]; then
|
||||
# Count LIB_SOURCES (library object files to compile)
|
||||
expected_lib_objects=$(grep -E '\.cc\s*\\?$' "$SRC_MK" | grep -v '^#' | wc -l)
|
||||
|
||||
# Count TEST_MAIN_SOURCES (test binaries to link)
|
||||
expected_test_binaries=$(sed -n '/^TEST_MAIN_SOURCES =/,/^[^ ]/p' "$SRC_MK" | grep -cE '\.cc\s*\\?$' 2>/dev/null || echo 0)
|
||||
else
|
||||
expected_lib_objects=0
|
||||
expected_test_binaries=0
|
||||
fi
|
||||
|
||||
# Check for test generation phase (t/ directory being created)
|
||||
if [[ -d "$T_DIR" ]]; then
|
||||
total=$(find "$T_DIR" -name 'run-*' -type f 2>/dev/null | wc -l)
|
||||
if [[ "$total" -gt 0 ]]; then
|
||||
output_json "running" 0 "$total" 0 0 0 "0" "" "generating"
|
||||
exit 0
|
||||
fi
|
||||
fi
|
||||
|
||||
# Count compiled object files (in subdirectories matching source structure)
|
||||
# Object files are created as dir/file.o (e.g., cache/cache.o, db/db_impl.o)
|
||||
compiled_objects=0
|
||||
if [[ "$expected_lib_objects" -gt 0 ]]; then
|
||||
# Count .o files in source directories
|
||||
compiled_objects=$(find cache db env file logging memory memtable monitoring options port table test_util trace_replay util utilities -name '*.o' -type f 2>/dev/null | wc -l)
|
||||
fi
|
||||
|
||||
# Count linked test binaries (test binaries are in current directory with _test suffix)
|
||||
linked_tests=0
|
||||
if [[ "$expected_test_binaries" -gt 0 ]]; then
|
||||
linked_tests=$(find . -maxdepth 1 -name '*_test' -type f -executable 2>/dev/null | wc -l)
|
||||
fi
|
||||
|
||||
# Determine phase based on what exists
|
||||
if [[ "$compiled_objects" -eq 0 && "$linked_tests" -eq 0 ]]; then
|
||||
# Nothing compiled yet - not started or just beginning
|
||||
output_json "not_started" 0 0 0 0 0 "0" ""
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Calculate total work units: compiling + linking
|
||||
total_work=$((expected_lib_objects + expected_test_binaries))
|
||||
completed_work=$((compiled_objects + linked_tests))
|
||||
|
||||
if [[ "$total_work" -gt 0 ]]; then
|
||||
percent=$((completed_work * 100 / total_work))
|
||||
else
|
||||
percent=0
|
||||
fi
|
||||
|
||||
# Determine phase
|
||||
if [[ "$compiled_objects" -lt "$expected_lib_objects" ]]; then
|
||||
phase="compiling"
|
||||
# Get most recently modified .o file as last_item
|
||||
last_item=$(find cache db env file logging memory memtable monitoring options port table test_util trace_replay util utilities -name '*.o' -type f -printf '%T@ %p\n' 2>/dev/null | sort -rn | head -1 | cut -d' ' -f2- | sed 's,^\./,,;s,\.o$,,')
|
||||
elif [[ "$linked_tests" -lt "$expected_test_binaries" ]]; then
|
||||
phase="linking"
|
||||
# Get most recently modified test binary as last_item
|
||||
last_item=$(find . -maxdepth 1 -name '*_test' -type f -executable -printf '%T@ %p\n' 2>/dev/null | sort -rn | head -1 | cut -d' ' -f2- | sed 's,^\./,,')
|
||||
else
|
||||
phase="generating"
|
||||
last_item=""
|
||||
fi
|
||||
|
||||
output_json "running" "$completed_work" "$total_work" 0 "$percent" 0 "0" "$last_item" "$phase"
|
||||
Executable
+137
@@ -0,0 +1,137 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Copyright (c) 2016, Facebook. All rights reserved.
|
||||
#
|
||||
# Overall wrapper script for RocksDB continuous builds. The implementation is a
|
||||
# trivial pulling scheme. We loop infinitely, check if any new changes have been
|
||||
# committed, if yes then trigger a Sandcastle run, and finally go to sleep again
|
||||
# for a certain interval.
|
||||
#
|
||||
|
||||
SRC_GIT_REPO=/data/git/rocksdb-public
|
||||
error=0
|
||||
|
||||
function log {
|
||||
DATE=`date +%Y-%m-%d:%H:%M:%S`
|
||||
# shellcheck disable=SC2068
|
||||
echo $DATE $@
|
||||
}
|
||||
|
||||
function log_err {
|
||||
# shellcheck disable=SC2145
|
||||
log "ERROR: $@ Error code: $error."
|
||||
}
|
||||
|
||||
function update_repo_status {
|
||||
# Update the parent first.
|
||||
pushd $SRC_GIT_REPO
|
||||
|
||||
# This is a fatal error. Something in the environment isn't right and we will
|
||||
# terminate the execution.
|
||||
error=$?
|
||||
if [ ! $error -eq 0 ]; then
|
||||
log_err "Where is $SRC_GIT_REPO?"
|
||||
exit $error
|
||||
fi
|
||||
|
||||
HTTPS_PROXY=fwdproxy:8080 git fetch -f
|
||||
|
||||
error=$?
|
||||
if [ ! $error -eq 0 ]; then
|
||||
log_err "git fetch -f failed."
|
||||
popd
|
||||
return $error
|
||||
fi
|
||||
|
||||
git update-ref refs/heads/master refs/remotes/origin/master
|
||||
|
||||
error=$?
|
||||
if [ ! $error -eq 0 ]; then
|
||||
log_err "git update-ref failed."
|
||||
popd
|
||||
return $error
|
||||
fi
|
||||
|
||||
popd
|
||||
|
||||
# We're back in an instance-specific directory. Get the latest changes.
|
||||
git pull --rebase
|
||||
|
||||
error=$?
|
||||
if [ ! $error -eq 0 ]; then
|
||||
log_err "git pull --rebase failed."
|
||||
return $error
|
||||
fi
|
||||
}
|
||||
|
||||
#
|
||||
# Execution starts here.
|
||||
#
|
||||
|
||||
# Path to the determinator from the root of the RocksDB repo.
|
||||
CONTRUN_DETERMINATOR=./build_tools/RocksDBCommonHelper.php
|
||||
|
||||
# Value of the previous commit.
|
||||
PREV_COMMIT=
|
||||
|
||||
log "Starting to monitor for new RocksDB changes ..."
|
||||
log "Running under `pwd` as `whoami`."
|
||||
|
||||
# Paranoia. Make sure that we're using the right branch.
|
||||
git checkout master
|
||||
|
||||
error=$?
|
||||
if [ ! $error -eq 0 ]; then
|
||||
log_err "This is not good. Can't checkout master. Bye-bye!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# We'll run forever and let the execution environment terminate us if we'll
|
||||
# exceed whatever timeout is set for the job.
|
||||
while true;
|
||||
do
|
||||
# Get the latest changes committed.
|
||||
update_repo_status
|
||||
|
||||
error=$?
|
||||
if [ $error -eq 0 ]; then
|
||||
LAST_COMMIT=`git log -1 | head -1 | grep commit | awk '{ print $2; }'`
|
||||
|
||||
log "Last commit is '$LAST_COMMIT', previous commit is '$PREV_COMMIT'."
|
||||
|
||||
if [ "$PREV_COMMIT" == "$LAST_COMMIT" ]; then
|
||||
log "There were no changes since the last time I checked. Going to sleep."
|
||||
else
|
||||
if [ ! -z "$LAST_COMMIT" ]; then
|
||||
log "New code has been committed or previous commit not known. " \
|
||||
"Will trigger the tests."
|
||||
|
||||
PREV_COMMIT=$LAST_COMMIT
|
||||
log "Updated previous commit to '$PREV_COMMIT'."
|
||||
|
||||
#
|
||||
# This is where we'll trigger the Sandcastle run. The values for
|
||||
# HTTPS_APP_VALUE and HTTPS_APP_VALUE will be set in the container we're
|
||||
# running in.
|
||||
#
|
||||
POST_RECEIVE_HOOK=1 php $CONTRUN_DETERMINATOR
|
||||
|
||||
error=$?
|
||||
if [ $error -eq 0 ]; then
|
||||
log "Sandcastle run successfully triggered."
|
||||
else
|
||||
log_err "Failed to trigger Sandcastle run."
|
||||
fi
|
||||
else
|
||||
log_err "Previous commit not updated. Don't know what the last one is."
|
||||
fi
|
||||
fi
|
||||
else
|
||||
log_err "Getting latest changes failed. Will skip running tests for now."
|
||||
fi
|
||||
|
||||
# Always sleep, even if errors happens while trying to determine the latest
|
||||
# commit. This will prevent us terminating in case of transient errors.
|
||||
log "Will go to sleep for 5 minutes."
|
||||
sleep 5m
|
||||
done
|
||||
@@ -0,0 +1,19 @@
|
||||
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
|
||||
GCC_BASE=/mnt/gvfs/third-party2/gcc/112ec378fec7002ad3e09afde022e656049f7191/5.x/centos7-native/c447969
|
||||
CLANG_BASE=/mnt/gvfs/third-party2/llvm-fb/04999bdb3ce81a11073535dcb00b5e13dc1cbaf5/stable/centos7-native/c9f9104
|
||||
LIBGCC_BASE=/mnt/gvfs/third-party2/libgcc/92b0c8e5c8eecc71eb042594ce1ab3413799b385/5.x/gcc-5-glibc-2.23/339d858
|
||||
GLIBC_BASE=/mnt/gvfs/third-party2/glibc/3d8698d5973ba94f41620a80a67e4457fdf01e90/2.23/gcc-5-glibc-2.23/ca1d1c0
|
||||
SNAPPY_BASE=/mnt/gvfs/third-party2/snappy/7f9bdaada18f59bc27ec2b0871eb8a6144343aef/1.1.3/gcc-5-glibc-2.23/9bc6787
|
||||
ZLIB_BASE=/mnt/gvfs/third-party2/zlib/22c2d65676fb7c23cfa797c4f6937f38b026f3cf/1.2.8/gcc-5-glibc-2.23/9bc6787
|
||||
BZIP2_BASE=/mnt/gvfs/third-party2/bzip2/dc49a21c5fceec6456a7a28a94dcd16690af1337/1.0.6/gcc-5-glibc-2.23/9bc6787
|
||||
LZ4_BASE=/mnt/gvfs/third-party2/lz4/907b498203d297947f3bb70b9466f47e100f1873/r131/gcc-5-glibc-2.23/9bc6787
|
||||
ZSTD_BASE=/mnt/gvfs/third-party2/zstd/af6628a46758f1a15484a1760cd7294164bc5ba1/1.3.5/gcc-5-glibc-2.23/03859b5
|
||||
GFLAGS_BASE=/mnt/gvfs/third-party2/gflags/0b9929d2588991c65a57168bf88aff2db87c5d48/2.2.0/gcc-5-glibc-2.23/9bc6787
|
||||
JEMALLOC_BASE=/mnt/gvfs/third-party2/jemalloc/b1a0e56c1e3e6929813a4331ade3a58ff083afbb/master/gcc-5-glibc-2.23/aa64d6b
|
||||
NUMA_BASE=/mnt/gvfs/third-party2/numa/9cbf2460284c669ed19c3ccb200a71f7dd7e53c7/2.0.11/gcc-5-glibc-2.23/9bc6787
|
||||
LIBUNWIND_BASE=/mnt/gvfs/third-party2/libunwind/bf3d7497fe4e6d007354f0adffa16ce3003f8338/1.3/gcc-5-glibc-2.23/b443de1
|
||||
TBB_BASE=/mnt/gvfs/third-party2/tbb/ff4e0b093534704d8abab678a4fd7f5ea7b094c7/2018_U5/gcc-5-glibc-2.23/9bc6787
|
||||
KERNEL_HEADERS_BASE=/mnt/gvfs/third-party2/kernel-headers/b5c4a61a5c483ba24722005ae07895971a2ac707/4.0.9-36_fbk5_2933_gd092e3f/gcc-5-glibc-2.23/da39a3e
|
||||
BINUTILS_BASE=/mnt/gvfs/third-party2/binutils/55031de95a2b46c82948743419a603b3d6aefe28/2.29.1/centos7-native/da39a3e
|
||||
VALGRIND_BASE=/mnt/gvfs/third-party2/valgrind/f3f697a28122e6bcd513273dd9c1ff23852fc59f/3.13.0/gcc-5-glibc-2.23/9bc6787
|
||||
LUA_BASE=/mnt/gvfs/third-party2/lua/f0cd714433206d5139df61659eb7b28b1dea6683/5.2.3/gcc-5-glibc-2.23/65372bd
|
||||
@@ -0,0 +1,20 @@
|
||||
# shellcheck disable=SC2148
|
||||
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
|
||||
GCC_BASE=/mnt/gvfs/third-party2/gcc/cf7d14c625ce30bae1a4661c2319c5a283e4dd22/4.8.1/centos6-native/cc6c9dc
|
||||
CLANG_BASE=/mnt/gvfs/third-party2/llvm-fb/8598c375b0e94e1448182eb3df034704144a838d/stable/centos6-native/3f16ddd
|
||||
LIBGCC_BASE=/mnt/gvfs/third-party2/libgcc/d6e0a7da6faba45f5e5b1638f9edd7afc2f34e7d/4.8.1/gcc-4.8.1-glibc-2.17/8aac7fc
|
||||
GLIBC_BASE=/mnt/gvfs/third-party2/glibc/d282e6e8f3d20f4e40a516834847bdc038e07973/2.17/gcc-4.8.1-glibc-2.17/99df8fc
|
||||
SNAPPY_BASE=/mnt/gvfs/third-party2/snappy/8c38a4c1e52b4c2cc8a9cdc31b9c947ed7dbfcb4/1.1.3/gcc-4.8.1-glibc-2.17/c3f970a
|
||||
ZLIB_BASE=/mnt/gvfs/third-party2/zlib/0882df3713c7a84f15abe368dc004581f20b39d7/1.2.8/gcc-4.8.1-glibc-2.17/c3f970a
|
||||
BZIP2_BASE=/mnt/gvfs/third-party2/bzip2/740325875f6729f42d28deaa2147b0854f3a347e/1.0.6/gcc-4.8.1-glibc-2.17/c3f970a
|
||||
LZ4_BASE=/mnt/gvfs/third-party2/lz4/0e790b441e2d9acd68d51e1d2e028f88c6a79ddf/r131/gcc-4.8.1-glibc-2.17/c3f970a
|
||||
ZSTD_BASE=/mnt/gvfs/third-party2/zstd/9455f75ff7f4831dc9fda02a6a0f8c68922fad8f/1.0.0/gcc-4.8.1-glibc-2.17/c3f970a
|
||||
GFLAGS_BASE=/mnt/gvfs/third-party2/gflags/f001a51b2854957676d07306ef3abf67186b5c8b/2.1.1/gcc-4.8.1-glibc-2.17/c3f970a
|
||||
JEMALLOC_BASE=/mnt/gvfs/third-party2/jemalloc/fc8a13ca1fffa4d0765c716c5a0b49f0c107518f/master/gcc-4.8.1-glibc-2.17/8d31e51
|
||||
NUMA_BASE=/mnt/gvfs/third-party2/numa/17c514c4d102a25ca15f4558be564eeed76f4b6a/2.0.8/gcc-4.8.1-glibc-2.17/c3f970a
|
||||
LIBUNWIND_BASE=/mnt/gvfs/third-party2/libunwind/ad576de2a1ea560c4d3434304f0fc4e079bede42/trunk/gcc-4.8.1-glibc-2.17/675d945
|
||||
TBB_BASE=/mnt/gvfs/third-party2/tbb/9d9a554877d0c5bef330fe818ab7178806dd316a/4.0_update2/gcc-4.8.1-glibc-2.17/c3f970a
|
||||
KERNEL_HEADERS_BASE=/mnt/gvfs/third-party2/kernel-headers/7c111ff27e0c466235163f00f280a9d617c3d2ec/4.0.9-36_fbk5_2933_gd092e3f/gcc-4.8.1-glibc-2.17/da39a3e
|
||||
BINUTILS_BASE=/mnt/gvfs/third-party2/binutils/b7fd454c4b10c6a81015d4524ed06cdeab558490/2.26/centos6-native/da39a3e
|
||||
VALGRIND_BASE=/mnt/gvfs/third-party2/valgrind/d7f4d4d86674a57668e3a96f76f0e17dd0eb8765/3.8.1/gcc-4.8.1-glibc-2.17/c3f970a
|
||||
LUA_BASE=/mnt/gvfs/third-party2/lua/61e4abf5813bbc39bc4f548757ccfcadde175a48/5.2.3/centos6-native/730f94e
|
||||
@@ -0,0 +1,19 @@
|
||||
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
|
||||
GCC_BASE=/mnt/gvfs/third-party2/gcc/6e8e715624fd15256a7970073387793dfcf79b46/7.x/centos7-native/b2ef2b6
|
||||
CLANG_BASE=/mnt/gvfs/third-party2/llvm-fb/ef37e1faa1c29782abfac1ae65a291b9b7966f6d/stable/centos7-native/c9f9104
|
||||
LIBGCC_BASE=/mnt/gvfs/third-party2/libgcc/c67031f0f739ac61575a061518d6ef5038f99f90/7.x/platform007/5620abc
|
||||
GLIBC_BASE=/mnt/gvfs/third-party2/glibc/60d6f124a78798b73944f5ba87c2306ae3460153/2.26/platform007/f259413
|
||||
SNAPPY_BASE=/mnt/gvfs/third-party2/snappy/7f9bdaada18f59bc27ec2b0871eb8a6144343aef/1.1.3/platform007/ca4da3d
|
||||
ZLIB_BASE=/mnt/gvfs/third-party2/zlib/22c2d65676fb7c23cfa797c4f6937f38b026f3cf/1.2.8/platform007/ca4da3d
|
||||
BZIP2_BASE=/mnt/gvfs/third-party2/bzip2/dc49a21c5fceec6456a7a28a94dcd16690af1337/1.0.6/platform007/ca4da3d
|
||||
LZ4_BASE=/mnt/gvfs/third-party2/lz4/907b498203d297947f3bb70b9466f47e100f1873/r131/platform007/ca4da3d
|
||||
ZSTD_BASE=/mnt/gvfs/third-party2/zstd/3ee276cbacfad3074e3f07bf826ac47f06970f4e/1.3.5/platform007/15a3614
|
||||
GFLAGS_BASE=/mnt/gvfs/third-party2/gflags/0b9929d2588991c65a57168bf88aff2db87c5d48/2.2.0/platform007/ca4da3d
|
||||
JEMALLOC_BASE=/mnt/gvfs/third-party2/jemalloc/9c910d36d6235cc40e8ff559358f1833452300ca/master/platform007/5b0f53e
|
||||
NUMA_BASE=/mnt/gvfs/third-party2/numa/9cbf2460284c669ed19c3ccb200a71f7dd7e53c7/2.0.11/platform007/ca4da3d
|
||||
LIBUNWIND_BASE=/mnt/gvfs/third-party2/libunwind/bf3d7497fe4e6d007354f0adffa16ce3003f8338/1.3/platform007/6f3e0a9
|
||||
TBB_BASE=/mnt/gvfs/third-party2/tbb/ff4e0b093534704d8abab678a4fd7f5ea7b094c7/2018_U5/platform007/ca4da3d
|
||||
KERNEL_HEADERS_BASE=/mnt/gvfs/third-party2/kernel-headers/b5c4a61a5c483ba24722005ae07895971a2ac707/fb/platform007/da39a3e
|
||||
BINUTILS_BASE=/mnt/gvfs/third-party2/binutils/92ff90349e2f43ea0a8246d8b1cf17b6869013e3/2.29.1/centos7-native/da39a3e
|
||||
VALGRIND_BASE=/mnt/gvfs/third-party2/valgrind/f3f697a28122e6bcd513273dd9c1ff23852fc59f/3.13.0/platform007/ca4da3d
|
||||
LUA_BASE=/mnt/gvfs/third-party2/lua/f0cd714433206d5139df61659eb7b28b1dea6683/5.3.4/platform007/5007832
|
||||
@@ -1,20 +0,0 @@
|
||||
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
|
||||
# The file is generated using update_dependencies.sh.
|
||||
GCC_BASE=/mnt/gvfs/third-party2/gcc/e9cb2d4550b5a95024125084420342d256b349cd/11.x/centos9-native/3bed279
|
||||
CLANG_BASE=/mnt/gvfs/third-party2/llvm-fb/4e46e6967e15412702e51960c69790a78933856f/21/platform010/72a2ff8
|
||||
LIBGCC_BASE=/mnt/gvfs/third-party2/libgcc/63c5179edc90fec473e53c1b2f92269c65c39e07/11.x/platform010/5684a5a
|
||||
GLIBC_BASE=/mnt/gvfs/third-party2/glibc/b4a6248a76a163bcccb1e4751e247712c535846e/2.34/platform010/f259413
|
||||
SNAPPY_BASE=/mnt/gvfs/third-party2/snappy/ef895164499fdfedb9d9b9c85520de10aa8ff9ca/1.1.8/platform010/76ebdda
|
||||
ZLIB_BASE=/mnt/gvfs/third-party2/zlib/643dbf0136f869562485cfcb2953f5cab60447ef/1.2.8/platform010/76ebdda
|
||||
BZIP2_BASE=/mnt/gvfs/third-party2/bzip2/09703139cfc376bd8a82642385a0e97726b28287/1.0.6/platform010/76ebdda
|
||||
LZ4_BASE=/mnt/gvfs/third-party2/lz4/823bf8e000654abef784311296ffc8166d9d3986/1.9.4/platform010/76ebdda
|
||||
ZSTD_BASE=/mnt/gvfs/third-party2/zstd/0cda78647b5712bec7ec6d9ba2f0dcdabfb6f44f/1.4.x/platform010/64091f4
|
||||
GFLAGS_BASE=/mnt/gvfs/third-party2/gflags/a02e0de00945729ad553c187c64e86b57f45a33b/2.2.0/platform010/76ebdda
|
||||
JEMALLOC_BASE=/mnt/gvfs/third-party2/jemalloc/a1a50742bec7195bfafb594e174ea45dc197a420/master/platform010/779a252
|
||||
NUMA_BASE=/mnt/gvfs/third-party2/numa/5b602edd46fda54cdd7ea45f77dbe4061206e174/2.0.11/platform010/76ebdda
|
||||
LIBUNWIND_BASE=/mnt/gvfs/third-party2/libunwind/f3cce2e1a79ac8d64e102886603a6997d6e88640/1.8/platform010/76ebdda
|
||||
LIBURING_BASE=/mnt/gvfs/third-party2/liburing/a98e2d137007e3ebf7f33bd6f99c2c56bdaf8488/20210212/platform010/76ebdda
|
||||
BENCHMARK_BASE=/mnt/gvfs/third-party2/benchmark/780c7a0f9cf0967961e69ad08e61cddd85d61821/trunk/platform010/76ebdda
|
||||
KERNEL_HEADERS_BASE=/mnt/gvfs/third-party2/kernel-headers/0883cccda758e68b47e4c04e4fa01142e1f60f32/fb/platform010/da39a3e
|
||||
BINUTILS_BASE=/mnt/gvfs/third-party2/binutils/1d5d19135a406a941d3a441eb2bf506bb9d19210/2.43/centos9-native/be7139e
|
||||
VALGRIND_BASE=/mnt/gvfs/third-party2/valgrind/3e2b7ccc315c9a0f2b678ce6fd774f4970f9bf3b/3.22.0/platform010/76ebdda
|
||||
+62
-68
@@ -3,37 +3,40 @@
|
||||
# COPYING file in the root directory) and Apache 2.0 License
|
||||
# (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
"""Filter for error messages in test output:
|
||||
'''Filter for error messages in test output:
|
||||
- Receives merged stdout/stderr from test on stdin
|
||||
- Finds patterns of known error messages for test name (first argument)
|
||||
- Prints those error messages to stdout
|
||||
"""
|
||||
'''
|
||||
|
||||
from __future__ import absolute_import
|
||||
from __future__ import division
|
||||
from __future__ import print_function
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import re
|
||||
import sys
|
||||
|
||||
|
||||
class ErrorParserBase:
|
||||
class ErrorParserBase(object):
|
||||
def parse_error(self, line):
|
||||
"""Parses a line of test output. If it contains an error, returns a
|
||||
'''Parses a line of test output. If it contains an error, returns a
|
||||
formatted message describing the error; otherwise, returns None.
|
||||
Subclasses must override this method.
|
||||
"""
|
||||
'''
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class GTestErrorParser(ErrorParserBase):
|
||||
"""A parser that remembers the last test that began running so it can print
|
||||
'''A parser that remembers the last test that began running so it can print
|
||||
that test's name upon detecting failure.
|
||||
"""
|
||||
|
||||
_GTEST_NAME_PATTERN = re.compile(r"\[ RUN \] (\S+)$")
|
||||
'''
|
||||
_GTEST_NAME_PATTERN = re.compile(r'\[ RUN \] (\S+)$')
|
||||
# format: '<filename or "unknown file">:<line #>: Failure'
|
||||
_GTEST_FAIL_PATTERN = re.compile(r"(unknown file|\S+:\d+): Failure$")
|
||||
_GTEST_FAIL_PATTERN = re.compile(r'(unknown file|\S+:\d+): Failure$')
|
||||
|
||||
def __init__(self):
|
||||
self._last_gtest_name = "Unknown test"
|
||||
self._last_gtest_name = 'Unknown test'
|
||||
|
||||
def parse_error(self, line):
|
||||
gtest_name_match = self._GTEST_NAME_PATTERN.match(line)
|
||||
@@ -42,13 +45,14 @@ class GTestErrorParser(ErrorParserBase):
|
||||
return None
|
||||
gtest_fail_match = self._GTEST_FAIL_PATTERN.match(line)
|
||||
if gtest_fail_match:
|
||||
return "{} failed: {}".format(self._last_gtest_name, gtest_fail_match.group(1))
|
||||
return '%s failed: %s' % (
|
||||
self._last_gtest_name, gtest_fail_match.group(1))
|
||||
return None
|
||||
|
||||
|
||||
class MatchErrorParser(ErrorParserBase):
|
||||
"""A simple parser that returns the whole line if it matches the pattern."""
|
||||
|
||||
'''A simple parser that returns the whole line if it matches the pattern.
|
||||
'''
|
||||
def __init__(self, pattern):
|
||||
self._pattern = re.compile(pattern)
|
||||
|
||||
@@ -65,104 +69,94 @@ class CompilerErrorParser(MatchErrorParser):
|
||||
# format (link error):
|
||||
# '<filename>:<line #>: error: <error msg>'
|
||||
# The below regex catches both
|
||||
super().__init__(r"\S+:\d+: error:")
|
||||
super(CompilerErrorParser, self).__init__(r'\S+:\d+: error:')
|
||||
|
||||
|
||||
class ScanBuildErrorParser(MatchErrorParser):
|
||||
def __init__(self):
|
||||
super().__init__(r"scan-build: \d+ bugs found.$")
|
||||
super(ScanBuildErrorParser, self).__init__(
|
||||
r'scan-build: \d+ bugs found.$')
|
||||
|
||||
|
||||
class DbCrashErrorParser(MatchErrorParser):
|
||||
def __init__(self):
|
||||
super().__init__(r"\*\*\*.*\^$|TEST FAILED.")
|
||||
super(DbCrashErrorParser, self).__init__(r'\*\*\*.*\^$|TEST FAILED.')
|
||||
|
||||
|
||||
class WriteStressErrorParser(MatchErrorParser):
|
||||
def __init__(self):
|
||||
super().__init__(
|
||||
r"ERROR: write_stress died with exitcode=\d+"
|
||||
)
|
||||
super(WriteStressErrorParser, self).__init__(
|
||||
r'ERROR: write_stress died with exitcode=\d+')
|
||||
|
||||
|
||||
class AsanErrorParser(MatchErrorParser):
|
||||
def __init__(self):
|
||||
super().__init__(r"==\d+==ERROR: AddressSanitizer:")
|
||||
super(AsanErrorParser, self).__init__(
|
||||
r'==\d+==ERROR: AddressSanitizer:')
|
||||
|
||||
|
||||
class UbsanErrorParser(MatchErrorParser):
|
||||
def __init__(self):
|
||||
# format: '<filename>:<line #>:<column #>: runtime error: <error msg>'
|
||||
super().__init__(r"\S+:\d+:\d+: runtime error:")
|
||||
super(UbsanErrorParser, self).__init__(r'\S+:\d+:\d+: runtime error:')
|
||||
|
||||
|
||||
class ValgrindErrorParser(MatchErrorParser):
|
||||
def __init__(self):
|
||||
# just grab the summary, valgrind doesn't clearly distinguish errors
|
||||
# from other log messages.
|
||||
super().__init__(r"==\d+== ERROR SUMMARY:")
|
||||
super(ValgrindErrorParser, self).__init__(r'==\d+== ERROR SUMMARY:')
|
||||
|
||||
|
||||
class CompatErrorParser(MatchErrorParser):
|
||||
def __init__(self):
|
||||
super().__init__(r"==== .*[Ee]rror.* ====$")
|
||||
super(CompatErrorParser, self).__init__(r'==== .*[Ee]rror.* ====$')
|
||||
|
||||
|
||||
class TsanErrorParser(MatchErrorParser):
|
||||
def __init__(self):
|
||||
super().__init__(r"WARNING: ThreadSanitizer:")
|
||||
super(TsanErrorParser, self).__init__(r'WARNING: ThreadSanitizer:')
|
||||
|
||||
|
||||
_TEST_NAME_TO_PARSERS = {
|
||||
"punit": [CompilerErrorParser, GTestErrorParser],
|
||||
"unit": [CompilerErrorParser, GTestErrorParser],
|
||||
"release": [CompilerErrorParser, GTestErrorParser],
|
||||
"unit_481": [CompilerErrorParser, GTestErrorParser],
|
||||
"release_481": [CompilerErrorParser, GTestErrorParser],
|
||||
"clang_unit": [CompilerErrorParser, GTestErrorParser],
|
||||
"clang_release": [CompilerErrorParser, GTestErrorParser],
|
||||
"clang_analyze": [CompilerErrorParser, ScanBuildErrorParser],
|
||||
"code_cov": [CompilerErrorParser, GTestErrorParser],
|
||||
"unity": [CompilerErrorParser, GTestErrorParser],
|
||||
"lite": [CompilerErrorParser],
|
||||
"lite_test": [CompilerErrorParser, GTestErrorParser],
|
||||
"stress_crash": [CompilerErrorParser, DbCrashErrorParser],
|
||||
"stress_crash_with_atomic_flush": [CompilerErrorParser, DbCrashErrorParser],
|
||||
"stress_crash_with_txn": [CompilerErrorParser, DbCrashErrorParser],
|
||||
"write_stress": [CompilerErrorParser, WriteStressErrorParser],
|
||||
"asan": [CompilerErrorParser, GTestErrorParser, AsanErrorParser],
|
||||
"asan_crash": [CompilerErrorParser, AsanErrorParser, DbCrashErrorParser],
|
||||
"asan_crash_with_atomic_flush": [
|
||||
CompilerErrorParser,
|
||||
AsanErrorParser,
|
||||
DbCrashErrorParser,
|
||||
],
|
||||
"asan_crash_with_txn": [CompilerErrorParser, AsanErrorParser, DbCrashErrorParser],
|
||||
"ubsan": [CompilerErrorParser, GTestErrorParser, UbsanErrorParser],
|
||||
"ubsan_crash": [CompilerErrorParser, UbsanErrorParser, DbCrashErrorParser],
|
||||
"ubsan_crash_with_atomic_flush": [
|
||||
CompilerErrorParser,
|
||||
UbsanErrorParser,
|
||||
DbCrashErrorParser,
|
||||
],
|
||||
"ubsan_crash_with_txn": [CompilerErrorParser, UbsanErrorParser, DbCrashErrorParser],
|
||||
"valgrind": [CompilerErrorParser, GTestErrorParser, ValgrindErrorParser],
|
||||
"tsan": [CompilerErrorParser, GTestErrorParser, TsanErrorParser],
|
||||
"format_compatible": [CompilerErrorParser, CompatErrorParser],
|
||||
"run_format_compatible": [CompilerErrorParser, CompatErrorParser],
|
||||
"no_compression": [CompilerErrorParser, GTestErrorParser],
|
||||
"run_no_compression": [CompilerErrorParser, GTestErrorParser],
|
||||
"regression": [CompilerErrorParser],
|
||||
"run_regression": [CompilerErrorParser],
|
||||
'punit': [CompilerErrorParser, GTestErrorParser],
|
||||
'unit': [CompilerErrorParser, GTestErrorParser],
|
||||
'release': [CompilerErrorParser, GTestErrorParser],
|
||||
'unit_481': [CompilerErrorParser, GTestErrorParser],
|
||||
'release_481': [CompilerErrorParser, GTestErrorParser],
|
||||
'clang_unit': [CompilerErrorParser, GTestErrorParser],
|
||||
'clang_release': [CompilerErrorParser, GTestErrorParser],
|
||||
'clang_analyze': [CompilerErrorParser, ScanBuildErrorParser],
|
||||
'code_cov': [CompilerErrorParser, GTestErrorParser],
|
||||
'unity': [CompilerErrorParser, GTestErrorParser],
|
||||
'lite': [CompilerErrorParser],
|
||||
'lite_test': [CompilerErrorParser, GTestErrorParser],
|
||||
'stress_crash': [CompilerErrorParser, DbCrashErrorParser],
|
||||
'stress_crash_with_atomic_flush': [CompilerErrorParser, DbCrashErrorParser],
|
||||
'write_stress': [CompilerErrorParser, WriteStressErrorParser],
|
||||
'asan': [CompilerErrorParser, GTestErrorParser, AsanErrorParser],
|
||||
'asan_crash': [CompilerErrorParser, AsanErrorParser, DbCrashErrorParser],
|
||||
'asan_crash_with_atomic_flush': [CompilerErrorParser, AsanErrorParser, DbCrashErrorParser],
|
||||
'ubsan': [CompilerErrorParser, GTestErrorParser, UbsanErrorParser],
|
||||
'ubsan_crash': [CompilerErrorParser, UbsanErrorParser, DbCrashErrorParser],
|
||||
'ubsan_crash_with_atomic_flush': [CompilerErrorParser, UbsanErrorParser, DbCrashErrorParser],
|
||||
'valgrind': [CompilerErrorParser, GTestErrorParser, ValgrindErrorParser],
|
||||
'tsan': [CompilerErrorParser, GTestErrorParser, TsanErrorParser],
|
||||
'format_compatible': [CompilerErrorParser, CompatErrorParser],
|
||||
'run_format_compatible': [CompilerErrorParser, CompatErrorParser],
|
||||
'no_compression': [CompilerErrorParser, GTestErrorParser],
|
||||
'run_no_compression': [CompilerErrorParser, GTestErrorParser],
|
||||
'regression': [CompilerErrorParser],
|
||||
'run_regression': [CompilerErrorParser],
|
||||
}
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) != 2:
|
||||
return "Usage: %s <test name>" % sys.argv[0]
|
||||
return 'Usage: %s <test name>' % sys.argv[0]
|
||||
test_name = sys.argv[1]
|
||||
if test_name not in _TEST_NAME_TO_PARSERS:
|
||||
return "Unknown test name: %s" % test_name
|
||||
return 'Unknown test name: %s' % test_name
|
||||
|
||||
error_parsers = []
|
||||
for parser_cls in _TEST_NAME_TO_PARSERS[test_name]:
|
||||
@@ -176,5 +170,5 @@ def main():
|
||||
print(error_msg)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
||||
|
||||
@@ -21,48 +21,38 @@ LIBGCC_LIBS=" -L $LIBGCC_BASE/lib"
|
||||
GLIBC_INCLUDE="$GLIBC_BASE/include"
|
||||
GLIBC_LIBS=" -L $GLIBC_BASE/lib"
|
||||
|
||||
if ! test $ROCKSDB_DISABLE_SNAPPY; then
|
||||
# snappy
|
||||
SNAPPY_INCLUDE=" -I $SNAPPY_BASE/include/"
|
||||
if test -z $PIC_BUILD; then
|
||||
SNAPPY_LIBS=" $SNAPPY_BASE/lib/libsnappy.a"
|
||||
else
|
||||
SNAPPY_LIBS=" $SNAPPY_BASE/lib/libsnappy_pic.a"
|
||||
fi
|
||||
CFLAGS+=" -DSNAPPY"
|
||||
# snappy
|
||||
SNAPPY_INCLUDE=" -I $SNAPPY_BASE/include/"
|
||||
if test -z $PIC_BUILD; then
|
||||
SNAPPY_LIBS=" $SNAPPY_BASE/lib/libsnappy.a"
|
||||
else
|
||||
SNAPPY_LIBS=" $SNAPPY_BASE/lib/libsnappy_pic.a"
|
||||
fi
|
||||
CFLAGS+=" -DSNAPPY"
|
||||
|
||||
if test -z $PIC_BUILD; then
|
||||
if ! test $ROCKSDB_DISABLE_ZLIB; then
|
||||
# location of zlib headers and libraries
|
||||
ZLIB_INCLUDE=" -I $ZLIB_BASE/include/"
|
||||
ZLIB_LIBS=" $ZLIB_BASE/lib/libz.a"
|
||||
CFLAGS+=" -DZLIB"
|
||||
fi
|
||||
# location of zlib headers and libraries
|
||||
ZLIB_INCLUDE=" -I $ZLIB_BASE/include/"
|
||||
ZLIB_LIBS=" $ZLIB_BASE/lib/libz.a"
|
||||
CFLAGS+=" -DZLIB"
|
||||
|
||||
if ! test $ROCKSDB_DISABLE_BZIP; then
|
||||
# location of bzip headers and libraries
|
||||
BZIP_INCLUDE=" -I $BZIP2_BASE/include/"
|
||||
BZIP_LIBS=" $BZIP2_BASE/lib/libbz2.a"
|
||||
CFLAGS+=" -DBZIP2"
|
||||
fi
|
||||
# location of bzip headers and libraries
|
||||
BZIP_INCLUDE=" -I $BZIP2_BASE/include/"
|
||||
BZIP_LIBS=" $BZIP2_BASE/lib/libbz2.a"
|
||||
CFLAGS+=" -DBZIP2"
|
||||
|
||||
if ! test $ROCKSDB_DISABLE_LZ4; then
|
||||
LZ4_INCLUDE=" -I $LZ4_BASE/include/"
|
||||
LZ4_LIBS=" $LZ4_BASE/lib/liblz4.a"
|
||||
CFLAGS+=" -DLZ4"
|
||||
fi
|
||||
LZ4_INCLUDE=" -I $LZ4_BASE/include/"
|
||||
LZ4_LIBS=" $LZ4_BASE/lib/liblz4.a"
|
||||
CFLAGS+=" -DLZ4"
|
||||
fi
|
||||
|
||||
if ! test $ROCKSDB_DISABLE_ZSTD; then
|
||||
ZSTD_INCLUDE=" -I $ZSTD_BASE/include/"
|
||||
if test -z $PIC_BUILD; then
|
||||
ZSTD_LIBS=" $ZSTD_BASE/lib/libzstd.a"
|
||||
else
|
||||
ZSTD_LIBS=" $ZSTD_BASE/lib/libzstd_pic.a"
|
||||
fi
|
||||
CFLAGS+=" -DZSTD -DZSTD_STATIC_LINKING_ONLY"
|
||||
ZSTD_INCLUDE=" -I $ZSTD_BASE/include/"
|
||||
if test -z $PIC_BUILD; then
|
||||
ZSTD_LIBS=" $ZSTD_BASE/lib/libzstd.a"
|
||||
else
|
||||
ZSTD_LIBS=" $ZSTD_BASE/lib/libzstd_pic.a"
|
||||
fi
|
||||
CFLAGS+=" -DZSTD -DZSTD_STATIC_LINKING_ONLY"
|
||||
|
||||
# location of gflags headers and libraries
|
||||
GFLAGS_INCLUDE=" -I $GFLAGS_BASE/include/"
|
||||
@@ -87,15 +77,23 @@ if test -z $PIC_BUILD; then
|
||||
LIBUNWIND="$LIBUNWIND_BASE/lib/libunwind.a"
|
||||
fi
|
||||
|
||||
test "$USE_SSE" || USE_SSE=1
|
||||
export USE_SSE
|
||||
test "$PORTABLE" || PORTABLE=1
|
||||
export PORTABLE
|
||||
# location of TBB
|
||||
TBB_INCLUDE=" -isystem $TBB_BASE/include/"
|
||||
if test -z $PIC_BUILD; then
|
||||
TBB_LIBS="$TBB_BASE/lib/libtbb.a"
|
||||
else
|
||||
TBB_LIBS="$TBB_BASE/lib/libtbb_pic.a"
|
||||
fi
|
||||
CFLAGS+=" -DTBB"
|
||||
|
||||
# use Intel SSE support for checksum calculations
|
||||
export USE_SSE=1
|
||||
export PORTABLE=1
|
||||
|
||||
BINUTILS="$BINUTILS_BASE/bin"
|
||||
AR="$BINUTILS/ar"
|
||||
|
||||
DEPS_INCLUDE="$SNAPPY_INCLUDE $ZLIB_INCLUDE $BZIP_INCLUDE $LZ4_INCLUDE $ZSTD_INCLUDE $GFLAGS_INCLUDE $NUMA_INCLUDE"
|
||||
DEPS_INCLUDE="$SNAPPY_INCLUDE $ZLIB_INCLUDE $BZIP_INCLUDE $LZ4_INCLUDE $ZSTD_INCLUDE $GFLAGS_INCLUDE $NUMA_INCLUDE $TBB_INCLUDE"
|
||||
|
||||
STDLIBS="-L $GCC_BASE/lib64"
|
||||
|
||||
@@ -104,13 +102,12 @@ CLANG_LIB="$CLANG_BASE/lib"
|
||||
CLANG_SRC="$CLANG_BASE/../../src"
|
||||
|
||||
CLANG_ANALYZER="$CLANG_BIN/clang++"
|
||||
CLANG_SCAN_BUILD="$CLANG_BIN/scan-build
|
||||
CLANG_SCAN_BUILD="$CLANG_SRC/llvm/tools/clang/tools/scan-build/bin/scan-build"
|
||||
|
||||
if [ -z "$USE_CLANG" ]; then
|
||||
# gcc
|
||||
CC="$GCC_BASE/bin/gcc"
|
||||
CXX="$GCC_BASE/bin/g++"
|
||||
AR="$GCC_BASE/bin/gcc-ar"
|
||||
|
||||
CFLAGS+=" -B$BINUTILS/gold"
|
||||
CFLAGS+=" -isystem $GLIBC_INCLUDE"
|
||||
@@ -121,7 +118,6 @@ else
|
||||
CLANG_INCLUDE="$CLANG_LIB/clang/stable/include"
|
||||
CC="$CLANG_BIN/clang"
|
||||
CXX="$CLANG_BIN/clang++"
|
||||
AR="$CLANG_BIN/llvm-ar"
|
||||
|
||||
KERNEL_HEADERS_INCLUDE="$KERNEL_HEADERS_BASE/include"
|
||||
|
||||
@@ -138,21 +134,29 @@ else
|
||||
fi
|
||||
|
||||
CFLAGS+=" $DEPS_INCLUDE"
|
||||
CFLAGS+=" -DROCKSDB_PLATFORM_POSIX -DROCKSDB_LIB_IO_POSIX -DROCKSDB_FALLOCATE_PRESENT -DROCKSDB_MALLOC_USABLE_SIZE -DROCKSDB_RANGESYNC_PRESENT -DROCKSDB_SCHED_GETCPU_PRESENT"
|
||||
CFLAGS+=" -DROCKSDB_PLATFORM_POSIX -DROCKSDB_LIB_IO_POSIX -DROCKSDB_FALLOCATE_PRESENT -DROCKSDB_MALLOC_USABLE_SIZE -DROCKSDB_RANGESYNC_PRESENT -DROCKSDB_SCHED_GETCPU_PRESENT -DROCKSDB_SUPPORT_THREAD_LOCAL -DHAVE_SSE42"
|
||||
CXXFLAGS+=" $CFLAGS"
|
||||
|
||||
EXEC_LDFLAGS=" $SNAPPY_LIBS $ZLIB_LIBS $BZIP_LIBS $LZ4_LIBS $ZSTD_LIBS $GFLAGS_LIBS $NUMA_LIB"
|
||||
EXEC_LDFLAGS=" $SNAPPY_LIBS $ZLIB_LIBS $BZIP_LIBS $LZ4_LIBS $ZSTD_LIBS $GFLAGS_LIBS $NUMA_LIB $TBB_LIBS"
|
||||
EXEC_LDFLAGS+=" -B$BINUTILS/gold"
|
||||
EXEC_LDFLAGS+=" -Wl,--dynamic-linker,/usr/local/fbcode/gcc-5-glibc-2.23/lib/ld.so"
|
||||
EXEC_LDFLAGS+=" $LIBUNWIND"
|
||||
EXEC_LDFLAGS+=" -Wl,-rpath=/usr/local/fbcode/gcc-5-glibc-2.23/lib"
|
||||
# dynamic loading
|
||||
# required by libtbb
|
||||
EXEC_LDFLAGS+=" -ldl"
|
||||
|
||||
PLATFORM_LDFLAGS="$LIBGCC_LIBS $GLIBC_LIBS $STDLIBS -lgcc -lstdc++"
|
||||
|
||||
EXEC_LDFLAGS_SHARED="$SNAPPY_LIBS $ZLIB_LIBS $BZIP_LIBS $LZ4_LIBS $ZSTD_LIBS $GFLAGS_LIBS"
|
||||
EXEC_LDFLAGS_SHARED="$SNAPPY_LIBS $ZLIB_LIBS $BZIP_LIBS $LZ4_LIBS $ZSTD_LIBS $GFLAGS_LIBS $TBB_LIBS"
|
||||
|
||||
VALGRIND_VER="$VALGRIND_BASE/bin/"
|
||||
|
||||
export CC CXX AR CFLAGS CXXFLAGS EXEC_LDFLAGS EXEC_LDFLAGS_SHARED VALGRIND_VER JEMALLOC_LIB JEMALLOC_INCLUDE CLANG_ANALYZER CLANG_SCAN_BUILD
|
||||
LUA_PATH="$LUA_BASE"
|
||||
|
||||
if test -z $PIC_BUILD; then
|
||||
LUA_LIB=" $LUA_PATH/lib/liblua.a"
|
||||
else
|
||||
LUA_LIB=" $LUA_PATH/lib/liblua_pic.a"
|
||||
fi
|
||||
|
||||
export CC CXX AR CFLAGS CXXFLAGS EXEC_LDFLAGS EXEC_LDFLAGS_SHARED VALGRIND_VER JEMALLOC_LIB JEMALLOC_INCLUDE CLANG_ANALYZER CLANG_SCAN_BUILD LUA_PATH LUA_LIB
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
#!/bin/sh
|
||||
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
|
||||
#
|
||||
# Set environment variables so that we can compile rocksdb using
|
||||
# fbcode settings. It uses the latest g++ compiler and also
|
||||
# uses jemalloc
|
||||
|
||||
BASEDIR=`dirname $BASH_SOURCE`
|
||||
source "$BASEDIR/dependencies_4.8.1.sh"
|
||||
|
||||
# location of libgcc
|
||||
LIBGCC_INCLUDE="$LIBGCC_BASE/include"
|
||||
LIBGCC_LIBS=" -L $LIBGCC_BASE/lib"
|
||||
|
||||
# location of glibc
|
||||
GLIBC_INCLUDE="$GLIBC_BASE/include"
|
||||
GLIBC_LIBS=" -L $GLIBC_BASE/lib"
|
||||
|
||||
# location of snappy headers and libraries
|
||||
SNAPPY_INCLUDE=" -I $SNAPPY_BASE/include"
|
||||
SNAPPY_LIBS=" $SNAPPY_BASE/lib/libsnappy.a"
|
||||
|
||||
# location of zlib headers and libraries
|
||||
ZLIB_INCLUDE=" -I $ZLIB_BASE/include"
|
||||
ZLIB_LIBS=" $ZLIB_BASE/lib/libz.a"
|
||||
|
||||
# location of bzip headers and libraries
|
||||
BZIP2_INCLUDE=" -I $BZIP2_BASE/include/"
|
||||
BZIP2_LIBS=" $BZIP2_BASE/lib/libbz2.a"
|
||||
|
||||
LZ4_INCLUDE=" -I $LZ4_BASE/include"
|
||||
LZ4_LIBS=" $LZ4_BASE/lib/liblz4.a"
|
||||
|
||||
ZSTD_INCLUDE=" -I $ZSTD_BASE/include"
|
||||
ZSTD_LIBS=" $ZSTD_BASE/lib/libzstd.a"
|
||||
|
||||
# location of gflags headers and libraries
|
||||
GFLAGS_INCLUDE=" -I $GFLAGS_BASE/include/"
|
||||
GFLAGS_LIBS=" $GFLAGS_BASE/lib/libgflags.a"
|
||||
|
||||
# location of jemalloc
|
||||
JEMALLOC_INCLUDE=" -I $JEMALLOC_BASE/include"
|
||||
JEMALLOC_LIB="$JEMALLOC_BASE/lib/libjemalloc.a"
|
||||
|
||||
# location of numa
|
||||
NUMA_INCLUDE=" -I $NUMA_BASE/include/"
|
||||
NUMA_LIB=" $NUMA_BASE/lib/libnuma.a"
|
||||
|
||||
# location of libunwind
|
||||
LIBUNWIND="$LIBUNWIND_BASE/lib/libunwind.a"
|
||||
|
||||
# location of tbb
|
||||
TBB_INCLUDE=" -isystem $TBB_BASE/include/"
|
||||
TBB_LIBS="$TBB_BASE/lib/libtbb.a"
|
||||
|
||||
# use Intel SSE support for checksum calculations
|
||||
export USE_SSE=1
|
||||
export PORTABLE=1
|
||||
|
||||
BINUTILS="$BINUTILS_BASE/bin"
|
||||
AR="$BINUTILS/ar"
|
||||
|
||||
DEPS_INCLUDE="$SNAPPY_INCLUDE $ZLIB_INCLUDE $BZIP2_INCLUDE $LZ4_INCLUDE $ZSTD_INCLUDE $GFLAGS_INCLUDE $NUMA_INCLUDE $TBB_INCLUDE"
|
||||
|
||||
STDLIBS="-L $GCC_BASE/lib64"
|
||||
|
||||
if [ -z "$USE_CLANG" ]; then
|
||||
# gcc
|
||||
CC="$GCC_BASE/bin/gcc"
|
||||
CXX="$GCC_BASE/bin/g++"
|
||||
|
||||
CFLAGS="-B$BINUTILS/gold -m64 -mtune=generic"
|
||||
CFLAGS+=" -isystem $GLIBC_INCLUDE"
|
||||
CFLAGS+=" -isystem $LIBGCC_INCLUDE"
|
||||
JEMALLOC=1
|
||||
else
|
||||
# clang
|
||||
CLANG_BIN="$CLANG_BASE/bin"
|
||||
CLANG_LIB="$CLANG_BASE/lib"
|
||||
CLANG_INCLUDE="$CLANG_LIB/clang/*/include"
|
||||
CC="$CLANG_BIN/clang"
|
||||
CXX="$CLANG_BIN/clang++"
|
||||
|
||||
KERNEL_HEADERS_INCLUDE="$KERNEL_HEADERS_BASE/include/"
|
||||
|
||||
CFLAGS="-B$BINUTILS/gold -nostdinc -nostdlib"
|
||||
CFLAGS+=" -isystem $LIBGCC_BASE/include/c++/4.8.1 "
|
||||
CFLAGS+=" -isystem $LIBGCC_BASE/include/c++/4.8.1/x86_64-facebook-linux "
|
||||
CFLAGS+=" -isystem $GLIBC_INCLUDE"
|
||||
CFLAGS+=" -isystem $LIBGCC_INCLUDE"
|
||||
CFLAGS+=" -isystem $CLANG_INCLUDE"
|
||||
CFLAGS+=" -isystem $KERNEL_HEADERS_INCLUDE/linux "
|
||||
CFLAGS+=" -isystem $KERNEL_HEADERS_INCLUDE "
|
||||
CXXFLAGS="-nostdinc++"
|
||||
fi
|
||||
|
||||
CFLAGS+=" $DEPS_INCLUDE"
|
||||
CFLAGS+=" -DROCKSDB_PLATFORM_POSIX -DROCKSDB_LIB_IO_POSIX -DROCKSDB_FALLOCATE_PRESENT -DROCKSDB_MALLOC_USABLE_SIZE -DROCKSDB_RANGESYNC_PRESENT -DROCKSDB_SCHED_GETCPU_PRESENT -DROCKSDB_SUPPORT_THREAD_LOCAL -DHAVE_SSE42"
|
||||
CFLAGS+=" -DSNAPPY -DGFLAGS=google -DZLIB -DBZIP2 -DLZ4 -DZSTD -DNUMA -DTBB"
|
||||
CXXFLAGS+=" $CFLAGS"
|
||||
|
||||
EXEC_LDFLAGS=" $SNAPPY_LIBS $ZLIB_LIBS $BZIP2_LIBS $LZ4_LIBS $ZSTD_LIBS $GFLAGS_LIBS $NUMA_LIB $TBB_LIBS"
|
||||
EXEC_LDFLAGS+=" -Wl,--dynamic-linker,/usr/local/fbcode/gcc-4.8.1-glibc-2.17/lib/ld.so"
|
||||
EXEC_LDFLAGS+=" $LIBUNWIND"
|
||||
EXEC_LDFLAGS+=" -Wl,-rpath=/usr/local/fbcode/gcc-4.8.1-glibc-2.17/lib"
|
||||
# required by libtbb
|
||||
EXEC_LDFLAGS+=" -ldl"
|
||||
|
||||
PLATFORM_LDFLAGS="$LIBGCC_LIBS $GLIBC_LIBS $STDLIBS -lgcc -lstdc++"
|
||||
|
||||
EXEC_LDFLAGS_SHARED="$SNAPPY_LIBS $ZLIB_LIBS $BZIP2_LIBS $LZ4_LIBS $ZSTD_LIBS $GFLAGS_LIBS"
|
||||
|
||||
VALGRIND_VER="$VALGRIND_BASE/bin/"
|
||||
|
||||
LUA_PATH="$LUA_BASE"
|
||||
|
||||
export CC CXX AR CFLAGS CXXFLAGS EXEC_LDFLAGS EXEC_LDFLAGS_SHARED VALGRIND_VER JEMALLOC_LIB JEMALLOC_INCLUDE LUA_PATH
|
||||
@@ -0,0 +1,158 @@
|
||||
#!/bin/sh
|
||||
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
|
||||
#
|
||||
# Set environment variables so that we can compile rocksdb using
|
||||
# fbcode settings. It uses the latest g++ and clang compilers and also
|
||||
# uses jemalloc
|
||||
# Environment variables that change the behavior of this script:
|
||||
# PIC_BUILD -- if true, it will only take pic versions of libraries from fbcode. libraries that don't have pic variant will not be included
|
||||
|
||||
|
||||
BASEDIR=`dirname $BASH_SOURCE`
|
||||
source "$BASEDIR/dependencies_platform007.sh"
|
||||
|
||||
CFLAGS=""
|
||||
|
||||
# libgcc
|
||||
LIBGCC_INCLUDE="$LIBGCC_BASE/include/c++/7.3.0"
|
||||
LIBGCC_LIBS=" -L $LIBGCC_BASE/lib"
|
||||
|
||||
# glibc
|
||||
GLIBC_INCLUDE="$GLIBC_BASE/include"
|
||||
GLIBC_LIBS=" -L $GLIBC_BASE/lib"
|
||||
|
||||
# snappy
|
||||
SNAPPY_INCLUDE=" -I $SNAPPY_BASE/include/"
|
||||
if test -z $PIC_BUILD; then
|
||||
SNAPPY_LIBS=" $SNAPPY_BASE/lib/libsnappy.a"
|
||||
else
|
||||
SNAPPY_LIBS=" $SNAPPY_BASE/lib/libsnappy_pic.a"
|
||||
fi
|
||||
CFLAGS+=" -DSNAPPY"
|
||||
|
||||
if test -z $PIC_BUILD; then
|
||||
# location of zlib headers and libraries
|
||||
ZLIB_INCLUDE=" -I $ZLIB_BASE/include/"
|
||||
ZLIB_LIBS=" $ZLIB_BASE/lib/libz.a"
|
||||
CFLAGS+=" -DZLIB"
|
||||
|
||||
# location of bzip headers and libraries
|
||||
BZIP_INCLUDE=" -I $BZIP2_BASE/include/"
|
||||
BZIP_LIBS=" $BZIP2_BASE/lib/libbz2.a"
|
||||
CFLAGS+=" -DBZIP2"
|
||||
|
||||
LZ4_INCLUDE=" -I $LZ4_BASE/include/"
|
||||
LZ4_LIBS=" $LZ4_BASE/lib/liblz4.a"
|
||||
CFLAGS+=" -DLZ4"
|
||||
fi
|
||||
|
||||
ZSTD_INCLUDE=" -I $ZSTD_BASE/include/"
|
||||
if test -z $PIC_BUILD; then
|
||||
ZSTD_LIBS=" $ZSTD_BASE/lib/libzstd.a"
|
||||
else
|
||||
ZSTD_LIBS=" $ZSTD_BASE/lib/libzstd_pic.a"
|
||||
fi
|
||||
CFLAGS+=" -DZSTD"
|
||||
|
||||
# location of gflags headers and libraries
|
||||
GFLAGS_INCLUDE=" -I $GFLAGS_BASE/include/"
|
||||
if test -z $PIC_BUILD; then
|
||||
GFLAGS_LIBS=" $GFLAGS_BASE/lib/libgflags.a"
|
||||
else
|
||||
GFLAGS_LIBS=" $GFLAGS_BASE/lib/libgflags_pic.a"
|
||||
fi
|
||||
CFLAGS+=" -DGFLAGS=gflags"
|
||||
|
||||
# location of jemalloc
|
||||
JEMALLOC_INCLUDE=" -I $JEMALLOC_BASE/include/"
|
||||
JEMALLOC_LIB=" $JEMALLOC_BASE/lib/libjemalloc.a"
|
||||
|
||||
if test -z $PIC_BUILD; then
|
||||
# location of numa
|
||||
NUMA_INCLUDE=" -I $NUMA_BASE/include/"
|
||||
NUMA_LIB=" $NUMA_BASE/lib/libnuma.a"
|
||||
CFLAGS+=" -DNUMA"
|
||||
|
||||
# location of libunwind
|
||||
LIBUNWIND="$LIBUNWIND_BASE/lib/libunwind.a"
|
||||
fi
|
||||
|
||||
# location of TBB
|
||||
TBB_INCLUDE=" -isystem $TBB_BASE/include/"
|
||||
if test -z $PIC_BUILD; then
|
||||
TBB_LIBS="$TBB_BASE/lib/libtbb.a"
|
||||
else
|
||||
TBB_LIBS="$TBB_BASE/lib/libtbb_pic.a"
|
||||
fi
|
||||
CFLAGS+=" -DTBB"
|
||||
|
||||
# use Intel SSE support for checksum calculations
|
||||
export USE_SSE=1
|
||||
export PORTABLE=1
|
||||
|
||||
BINUTILS="$BINUTILS_BASE/bin"
|
||||
AR="$BINUTILS/ar"
|
||||
|
||||
DEPS_INCLUDE="$SNAPPY_INCLUDE $ZLIB_INCLUDE $BZIP_INCLUDE $LZ4_INCLUDE $ZSTD_INCLUDE $GFLAGS_INCLUDE $NUMA_INCLUDE $TBB_INCLUDE"
|
||||
|
||||
STDLIBS="-L $GCC_BASE/lib64"
|
||||
|
||||
CLANG_BIN="$CLANG_BASE/bin"
|
||||
CLANG_LIB="$CLANG_BASE/lib"
|
||||
CLANG_SRC="$CLANG_BASE/../../src"
|
||||
|
||||
CLANG_ANALYZER="$CLANG_BIN/clang++"
|
||||
CLANG_SCAN_BUILD="$CLANG_SRC/llvm/tools/clang/tools/scan-build/bin/scan-build"
|
||||
|
||||
if [ -z "$USE_CLANG" ]; then
|
||||
# gcc
|
||||
CC="$GCC_BASE/bin/gcc"
|
||||
CXX="$GCC_BASE/bin/g++"
|
||||
|
||||
CFLAGS+=" -B$BINUTILS/gold"
|
||||
CFLAGS+=" -isystem $LIBGCC_INCLUDE"
|
||||
CFLAGS+=" -isystem $GLIBC_INCLUDE"
|
||||
JEMALLOC=1
|
||||
else
|
||||
# clang
|
||||
CLANG_INCLUDE="$CLANG_LIB/clang/stable/include"
|
||||
CC="$CLANG_BIN/clang"
|
||||
CXX="$CLANG_BIN/clang++"
|
||||
|
||||
KERNEL_HEADERS_INCLUDE="$KERNEL_HEADERS_BASE/include"
|
||||
|
||||
CFLAGS+=" -B$BINUTILS/gold -nostdinc -nostdlib"
|
||||
CFLAGS+=" -isystem $LIBGCC_BASE/include/c++/7.x "
|
||||
CFLAGS+=" -isystem $LIBGCC_BASE/include/c++/7.x/x86_64-facebook-linux "
|
||||
CFLAGS+=" -isystem $GLIBC_INCLUDE"
|
||||
CFLAGS+=" -isystem $LIBGCC_INCLUDE"
|
||||
CFLAGS+=" -isystem $CLANG_INCLUDE"
|
||||
CFLAGS+=" -isystem $KERNEL_HEADERS_INCLUDE/linux "
|
||||
CFLAGS+=" -isystem $KERNEL_HEADERS_INCLUDE "
|
||||
CFLAGS+=" -Wno-expansion-to-defined "
|
||||
CXXFLAGS="-nostdinc++"
|
||||
fi
|
||||
|
||||
CFLAGS+=" $DEPS_INCLUDE"
|
||||
CFLAGS+=" -DROCKSDB_PLATFORM_POSIX -DROCKSDB_LIB_IO_POSIX -DROCKSDB_FALLOCATE_PRESENT -DROCKSDB_MALLOC_USABLE_SIZE -DROCKSDB_RANGESYNC_PRESENT -DROCKSDB_SCHED_GETCPU_PRESENT -DROCKSDB_SUPPORT_THREAD_LOCAL -DHAVE_SSE42"
|
||||
CXXFLAGS+=" $CFLAGS"
|
||||
|
||||
EXEC_LDFLAGS=" $SNAPPY_LIBS $ZLIB_LIBS $BZIP_LIBS $LZ4_LIBS $ZSTD_LIBS $GFLAGS_LIBS $NUMA_LIB $TBB_LIBS"
|
||||
EXEC_LDFLAGS+=" -B$BINUTILS/gold"
|
||||
EXEC_LDFLAGS+=" -Wl,--dynamic-linker,/usr/local/fbcode/platform007/lib/ld.so"
|
||||
EXEC_LDFLAGS+=" $LIBUNWIND"
|
||||
EXEC_LDFLAGS+=" -Wl,-rpath=/usr/local/fbcode/platform007/lib"
|
||||
# required by libtbb
|
||||
EXEC_LDFLAGS+=" -ldl"
|
||||
|
||||
PLATFORM_LDFLAGS="$LIBGCC_LIBS $GLIBC_LIBS $STDLIBS -lgcc -lstdc++"
|
||||
|
||||
EXEC_LDFLAGS_SHARED="$SNAPPY_LIBS $ZLIB_LIBS $BZIP_LIBS $LZ4_LIBS $ZSTD_LIBS $GFLAGS_LIBS $TBB_LIBS"
|
||||
|
||||
VALGRIND_VER="$VALGRIND_BASE/bin/"
|
||||
|
||||
# lua not supported because it's on track for deprecation, I think
|
||||
LUA_PATH=
|
||||
LUA_LIB=
|
||||
|
||||
export CC CXX AR CFLAGS CXXFLAGS EXEC_LDFLAGS EXEC_LDFLAGS_SHARED VALGRIND_VER JEMALLOC_LIB JEMALLOC_INCLUDE CLANG_ANALYZER CLANG_SCAN_BUILD LUA_PATH LUA_LIB
|
||||
@@ -1,170 +0,0 @@
|
||||
#!/bin/sh
|
||||
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
|
||||
#
|
||||
# Set environment variables so that we can compile rocksdb using
|
||||
# fbcode settings. It uses the latest g++ and clang compilers and also
|
||||
# uses jemalloc
|
||||
# Environment variables that change the behavior of this script:
|
||||
# PIC_BUILD -- if true, it will only take pic versions of libraries from fbcode. libraries that don't have pic variant will not be included
|
||||
|
||||
|
||||
BASEDIR=`dirname $BASH_SOURCE`
|
||||
source "$BASEDIR/dependencies_platform010.sh"
|
||||
|
||||
# Disallow using libraries from default locations as they might not be compatible with platform010 libraries.
|
||||
CFLAGS=" --sysroot=/DOES/NOT/EXIST"
|
||||
|
||||
# libgcc
|
||||
LIBGCC_INCLUDE="$LIBGCC_BASE/include/c++/trunk"
|
||||
LIBGCC_LIBS=" -L $LIBGCC_BASE/lib -B$LIBGCC_BASE/lib/gcc/x86_64-facebook-linux/trunk/"
|
||||
|
||||
# glibc
|
||||
GLIBC_INCLUDE="$GLIBC_BASE/include"
|
||||
GLIBC_LIBS=" -L $GLIBC_BASE/lib"
|
||||
GLIBC_LIBS+=" -B$GLIBC_BASE/lib"
|
||||
|
||||
if test -z $PIC_BUILD; then
|
||||
MAYBE_PIC=
|
||||
else
|
||||
MAYBE_PIC=_pic
|
||||
fi
|
||||
|
||||
if ! test $ROCKSDB_DISABLE_SNAPPY; then
|
||||
# snappy
|
||||
SNAPPY_INCLUDE=" -I $SNAPPY_BASE/include/"
|
||||
SNAPPY_LIBS=" $SNAPPY_BASE/lib/libsnappy${MAYBE_PIC}.a"
|
||||
CFLAGS+=" -DSNAPPY"
|
||||
fi
|
||||
|
||||
if ! test $ROCKSDB_DISABLE_ZLIB; then
|
||||
# location of zlib headers and libraries
|
||||
ZLIB_INCLUDE=" -I $ZLIB_BASE/include/"
|
||||
ZLIB_LIBS=" $ZLIB_BASE/lib/libz${MAYBE_PIC}.a"
|
||||
CFLAGS+=" -DZLIB"
|
||||
fi
|
||||
|
||||
if ! test $ROCKSDB_DISABLE_BZIP; then
|
||||
# location of bzip headers and libraries
|
||||
BZIP_INCLUDE=" -I $BZIP2_BASE/include/"
|
||||
BZIP_LIBS=" $BZIP2_BASE/lib/libbz2${MAYBE_PIC}.a"
|
||||
CFLAGS+=" -DBZIP2"
|
||||
fi
|
||||
|
||||
if ! test $ROCKSDB_DISABLE_LZ4; then
|
||||
LZ4_INCLUDE=" -I $LZ4_BASE/include/"
|
||||
LZ4_LIBS=" $LZ4_BASE/lib/liblz4${MAYBE_PIC}.a"
|
||||
CFLAGS+=" -DLZ4"
|
||||
fi
|
||||
|
||||
if ! test $ROCKSDB_DISABLE_ZSTD; then
|
||||
ZSTD_INCLUDE=" -I $ZSTD_BASE/include/"
|
||||
ZSTD_LIBS=" $ZSTD_BASE/lib/libzstd${MAYBE_PIC}.a"
|
||||
CFLAGS+=" -DZSTD -DZSTD_STATIC_LINKING_ONLY"
|
||||
fi
|
||||
|
||||
# location of gflags headers and libraries
|
||||
GFLAGS_INCLUDE=" -I $GFLAGS_BASE/include/"
|
||||
GFLAGS_LIBS=" $GFLAGS_BASE/lib/libgflags${MAYBE_PIC}.a"
|
||||
CFLAGS+=" -DGFLAGS=gflags"
|
||||
|
||||
BENCHMARK_INCLUDE=" -I $BENCHMARK_BASE/include/"
|
||||
BENCHMARK_LIBS=" $BENCHMARK_BASE/lib/libbenchmark${MAYBE_PIC}.a"
|
||||
|
||||
# location of jemalloc
|
||||
JEMALLOC_INCLUDE=" -I $JEMALLOC_BASE/include/"
|
||||
JEMALLOC_LIB=" $JEMALLOC_BASE/lib/libjemalloc${MAYBE_PIC}.a"
|
||||
|
||||
# location of numa
|
||||
NUMA_INCLUDE=" -I $NUMA_BASE/include/"
|
||||
NUMA_LIB=" $NUMA_BASE/lib/libnuma${MAYBE_PIC}.a"
|
||||
CFLAGS+=" -DNUMA"
|
||||
|
||||
# location of libunwind
|
||||
LIBUNWIND="$LIBUNWIND_BASE/lib/libunwind${MAYBE_PIC}.a"
|
||||
|
||||
# location of LIBURING
|
||||
LIBURING_INCLUDE=" -isystem $LIBURING_BASE/include/"
|
||||
LIBURING_LIBS="$LIBURING_BASE/lib/liburing${MAYBE_PIC}.a"
|
||||
CFLAGS+=" -DLIBURING"
|
||||
|
||||
test "$USE_SSE" || USE_SSE=1
|
||||
export USE_SSE
|
||||
test "$PORTABLE" || PORTABLE=1
|
||||
export PORTABLE
|
||||
|
||||
BINUTILS="$BINUTILS_BASE/bin"
|
||||
AR="$BINUTILS/ar"
|
||||
AS="$BINUTILS/as"
|
||||
|
||||
DEPS_INCLUDE="$SNAPPY_INCLUDE $ZLIB_INCLUDE $BZIP_INCLUDE $LZ4_INCLUDE $ZSTD_INCLUDE $GFLAGS_INCLUDE $NUMA_INCLUDE $LIBURING_INCLUDE $BENCHMARK_INCLUDE"
|
||||
|
||||
STDLIBS="-L $GCC_BASE/lib64"
|
||||
|
||||
CLANG_BIN="$CLANG_BASE/bin"
|
||||
CLANG_LIB="$CLANG_BASE/lib"
|
||||
CLANG_SRC="$CLANG_BASE/../../src"
|
||||
|
||||
CLANG_ANALYZER="$CLANG_BIN/clang++"
|
||||
CLANG_SCAN_BUILD="$CLANG_BIN/scan-build"
|
||||
|
||||
if [ -z "$USE_CLANG" ]; then
|
||||
# gcc
|
||||
CC="$GCC_BASE/bin/gcc"
|
||||
CXX="$GCC_BASE/bin/g++"
|
||||
AR="$GCC_BASE/bin/gcc-ar"
|
||||
|
||||
CFLAGS+=" -B$BINUTILS -nostdinc -nostdlib"
|
||||
CFLAGS+=" -I$GCC_BASE/include"
|
||||
CFLAGS+=" -isystem $GCC_BASE/lib/gcc/x86_64-redhat-linux-gnu/11.2.1/include"
|
||||
CFLAGS+=" -isystem $GCC_BASE/lib/gcc/x86_64-redhat-linux-gnu/11.2.1/install-tools/include"
|
||||
CFLAGS+=" -isystem $GCC_BASE/lib/gcc/x86_64-redhat-linux-gnu/11.2.1/include-fixed/"
|
||||
CFLAGS+=" -isystem $LIBGCC_INCLUDE"
|
||||
CFLAGS+=" -isystem $GLIBC_INCLUDE"
|
||||
CFLAGS+=" -I$GLIBC_INCLUDE"
|
||||
CFLAGS+=" -I$LIBGCC_BASE/include"
|
||||
CFLAGS+=" -I$LIBGCC_BASE/include/c++/11.x/"
|
||||
CFLAGS+=" -I$LIBGCC_BASE/include/c++/11.x/x86_64-facebook-linux/"
|
||||
CFLAGS+=" -I$LIBGCC_BASE/include/c++/11.x/backward"
|
||||
CFLAGS+=" -isystem $GLIBC_INCLUDE -I$GLIBC_INCLUDE"
|
||||
JEMALLOC=1
|
||||
else
|
||||
# clang
|
||||
CLANG_INCLUDE="$CLANG_LIB/clang/stable/include"
|
||||
CC="$CLANG_BIN/clang"
|
||||
CXX="$CLANG_BIN/clang++"
|
||||
AR="$CLANG_BIN/llvm-ar"
|
||||
|
||||
CFLAGS+=" -B$BINUTILS -nostdinc -nostdlib"
|
||||
CFLAGS+=" -isystem $LIBGCC_BASE/include/c++/trunk "
|
||||
CFLAGS+=" -isystem $LIBGCC_BASE/include/c++/trunk/x86_64-facebook-linux "
|
||||
CFLAGS+=" -isystem $GLIBC_INCLUDE"
|
||||
CFLAGS+=" -isystem $LIBGCC_INCLUDE"
|
||||
CFLAGS+=" -isystem $CLANG_INCLUDE"
|
||||
CFLAGS+=" -Wno-expansion-to-defined "
|
||||
CXXFLAGS="-nostdinc++"
|
||||
fi
|
||||
|
||||
KERNEL_HEADERS_INCLUDE="$KERNEL_HEADERS_BASE/include"
|
||||
CFLAGS+=" -isystem $KERNEL_HEADERS_INCLUDE/linux "
|
||||
CFLAGS+=" -isystem $KERNEL_HEADERS_INCLUDE "
|
||||
|
||||
CFLAGS+=" $DEPS_INCLUDE"
|
||||
CFLAGS+=" -DROCKSDB_PLATFORM_POSIX -DROCKSDB_LIB_IO_POSIX -DROCKSDB_FALLOCATE_PRESENT -DROCKSDB_MALLOC_USABLE_SIZE -DROCKSDB_RANGESYNC_PRESENT -DROCKSDB_SCHED_GETCPU_PRESENT -DROCKSDB_IOURING_PRESENT"
|
||||
CXXFLAGS+=" $CFLAGS"
|
||||
|
||||
EXEC_LDFLAGS=" $SNAPPY_LIBS $ZLIB_LIBS $BZIP_LIBS $LZ4_LIBS $ZSTD_LIBS $GFLAGS_LIBS $NUMA_LIB $LIBURING_LIBS $BENCHMARK_LIBS"
|
||||
EXEC_LDFLAGS+=" -Wl,--dynamic-linker,/usr/local/fbcode/platform010/lib/ld.so"
|
||||
EXEC_LDFLAGS+=" $LIBUNWIND"
|
||||
EXEC_LDFLAGS+=" -Wl,-rpath=/usr/local/fbcode/platform010/lib"
|
||||
EXEC_LDFLAGS+=" -Wl,-rpath=$GCC_BASE/lib64"
|
||||
# dynamic loading
|
||||
EXEC_LDFLAGS+=" -ldl"
|
||||
|
||||
PLATFORM_LDFLAGS="$LIBGCC_LIBS $GLIBC_LIBS $STDLIBS -lgcc -lstdc++"
|
||||
PLATFORM_LDFLAGS+=" -B$BINUTILS"
|
||||
|
||||
EXEC_LDFLAGS_SHARED="$SNAPPY_LIBS $ZLIB_LIBS $BZIP_LIBS $LZ4_LIBS $ZSTD_LIBS $GFLAGS_LIBS $LIBURING_LIBS $BENCHMARK_LIBS"
|
||||
|
||||
VALGRIND_VER="$VALGRIND_BASE/bin/"
|
||||
|
||||
export CC CXX AR AS CFLAGS CXXFLAGS EXEC_LDFLAGS EXEC_LDFLAGS_SHARED VALGRIND_VER JEMALLOC_LIB JEMALLOC_INCLUDE CLANG_ANALYZER CLANG_SCAN_BUILD
|
||||
+48
-208
@@ -2,105 +2,36 @@
|
||||
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
|
||||
# If clang_format_diff.py command is not specfied, we assume we are able to
|
||||
# access directly without any path.
|
||||
if [ -z $CLANG_FORMAT_DIFF ]
|
||||
then
|
||||
CLANG_FORMAT_DIFF="clang-format-diff.py"
|
||||
fi
|
||||
|
||||
print_usage () {
|
||||
echo "Usage:"
|
||||
echo "format-diff.sh [OPTIONS]"
|
||||
echo "-c: check only."
|
||||
echo "-y: auto-apply formatting without prompts (non-interactive mode)."
|
||||
echo "-h: print this message."
|
||||
}
|
||||
# Check clang-format-diff.py
|
||||
if ! which $CLANG_FORMAT_DIFF &> /dev/null
|
||||
then
|
||||
echo "You didn't have clang-format-diff.py and/or clang-format available in your computer!"
|
||||
echo "You can download clang-format-diff.py by running: "
|
||||
echo " curl --location http://goo.gl/iUW1u2 -o ${CLANG_FORMAT_DIFF}"
|
||||
echo "You can download clang-format by running: "
|
||||
echo " brew install clang-format"
|
||||
echo "Then, move both files (i.e. ${CLANG_FORMAT_DIFF} and clang-format) to some directory within PATH=${PATH}"
|
||||
exit 128
|
||||
fi
|
||||
|
||||
while getopts ':cyh' OPTION; do
|
||||
case "$OPTION" in
|
||||
c)
|
||||
CHECK_ONLY=1
|
||||
;;
|
||||
y)
|
||||
AUTO_APPLY=1
|
||||
;;
|
||||
h)
|
||||
print_usage
|
||||
exit 1
|
||||
;;
|
||||
?)
|
||||
print_usage
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
# Check argparse, a library that clang-format-diff.py requires.
|
||||
python 2>/dev/null << EOF
|
||||
import argparse
|
||||
EOF
|
||||
|
||||
REPO_ROOT="$(git rev-parse --show-toplevel)"
|
||||
|
||||
if [ "$CLANG_FORMAT_DIFF" ]; then
|
||||
echo "Note: CLANG_FORMAT_DIFF='$CLANG_FORMAT_DIFF'"
|
||||
# Dry run to confirm dependencies like argparse
|
||||
if $CLANG_FORMAT_DIFF --help >/dev/null < /dev/null; then
|
||||
true #Good
|
||||
else
|
||||
exit 128
|
||||
fi
|
||||
else
|
||||
# First try directly executing the possibilities
|
||||
if clang-format-diff --help &> /dev/null < /dev/null; then
|
||||
CLANG_FORMAT_DIFF=clang-format-diff
|
||||
elif clang-format-diff.py --help &> /dev/null < /dev/null; then
|
||||
CLANG_FORMAT_DIFF=clang-format-diff.py
|
||||
elif $REPO_ROOT/clang-format-diff.py --help &> /dev/null < /dev/null; then
|
||||
CLANG_FORMAT_DIFF=$REPO_ROOT/clang-format-diff.py
|
||||
else
|
||||
# This probably means we need to directly invoke the interpreter.
|
||||
# But first find clang-format-diff.py
|
||||
if [ -f "$REPO_ROOT/clang-format-diff.py" ]; then
|
||||
CFD_PATH="$REPO_ROOT/clang-format-diff.py"
|
||||
elif which clang-format-diff.py &> /dev/null; then
|
||||
CFD_PATH="$(which clang-format-diff.py)"
|
||||
else
|
||||
echo "You didn't have clang-format-diff.py and/or clang-format available in your computer!"
|
||||
echo "You can download clang-format-diff.py by running: "
|
||||
echo " curl --location https://raw.githubusercontent.com/llvm/llvm-project/main/clang/tools/clang-format/clang-format-diff.py -o ${REPO_ROOT}/clang-format-diff.py"
|
||||
echo "You should make sure the downloaded script is not compromised."
|
||||
echo "You can download clang-format by running:"
|
||||
echo " brew install clang-format"
|
||||
echo " Or"
|
||||
echo " apt install clang-format"
|
||||
echo " This might work too:"
|
||||
echo " yum install git-clang-format"
|
||||
echo "Then make sure clang-format is available and executable from \$PATH:"
|
||||
echo " clang-format --version"
|
||||
exit 128
|
||||
fi
|
||||
# Check argparse pre-req on interpreter, or it will fail
|
||||
if echo import argparse | ${PYTHON:-python3}; then
|
||||
true # Good
|
||||
else
|
||||
echo "To run clang-format-diff.py, we'll need the library "argparse" to be"
|
||||
echo "installed. You can try either of the follow ways to install it:"
|
||||
echo " 1. Manually download argparse: https://pypi.python.org/pypi/argparse"
|
||||
echo " 2. easy_install argparse (if you have easy_install)"
|
||||
echo " 3. pip install argparse (if you have pip)"
|
||||
exit 129
|
||||
fi
|
||||
# Unfortunately, some machines have a Python2 clang-format-diff.py
|
||||
# installed but only a Python3 interpreter installed. Unfortunately,
|
||||
# automatic 2to3 migration is insufficient, so suggest downloading latest.
|
||||
if grep -q "print '" "$CFD_PATH" && \
|
||||
${PYTHON:-python3} --version | grep -q 'ython 3'; then
|
||||
echo "You have clang-format-diff.py for Python 2 but are using a Python 3"
|
||||
echo "interpreter (${PYTHON:-python3})."
|
||||
echo "You can download clang-format-diff.py for Python 3 by running: "
|
||||
echo " curl --location https://raw.githubusercontent.com/llvm/llvm-project/main/clang/tools/clang-format/clang-format-diff.py -o ${REPO_ROOT}/clang-format-diff.py"
|
||||
echo "You should make sure the downloaded script is not compromised."
|
||||
exit 130
|
||||
fi
|
||||
CLANG_FORMAT_DIFF="${PYTHON:-python3} $CFD_PATH"
|
||||
# This had better work after all those checks
|
||||
if $CLANG_FORMAT_DIFF --help >/dev/null < /dev/null; then
|
||||
true #Good
|
||||
else
|
||||
exit 128
|
||||
fi
|
||||
fi
|
||||
if [ "$?" != 0 ]
|
||||
then
|
||||
echo "To run clang-format-diff.py, we'll need the library "argparse" to be"
|
||||
echo "installed. You can try either of the follow ways to install it:"
|
||||
echo " 1. Manually download argparse: https://pypi.python.org/pypi/argparse"
|
||||
echo " 2. easy_install argparse (if you have easy_install)"
|
||||
echo " 3. pip install argparse (if you have pip)"
|
||||
exit 129
|
||||
fi
|
||||
|
||||
# TODO(kailiu) following work is not complete since we still need to figure
|
||||
@@ -122,139 +53,49 @@ fi
|
||||
# fi
|
||||
set -e
|
||||
|
||||
# Exclude third-party from formatting
|
||||
EXCLUDE=':!third-party/'
|
||||
|
||||
uncommitted_code=`git diff HEAD`
|
||||
LAST_MASTER=`git merge-base master HEAD`
|
||||
|
||||
# If there's no uncommitted changes, we assume user are doing post-commit
|
||||
# format check, in which case we'll try to check the modified lines vs. the
|
||||
# facebook/rocksdb.git main branch. Otherwise, we'll check format of the
|
||||
# uncommitted code only.
|
||||
# format check, in which case we'll check the modified lines since last commit
|
||||
# from master. Otherwise, we'll check format of the uncommitted code only.
|
||||
if [ -z "$uncommitted_code" ]
|
||||
then
|
||||
# Attempt to get name of facebook/rocksdb.git remote.
|
||||
[ "$FORMAT_REMOTE" ] || FORMAT_REMOTE="$(LC_ALL=POSIX LANG=POSIX git remote -v | grep 'facebook/rocksdb.git' | head -n 1 | cut -f 1)"
|
||||
# Fall back on 'origin' if that fails
|
||||
[ "$FORMAT_REMOTE" ] || FORMAT_REMOTE=origin
|
||||
# Use main branch from that remote
|
||||
[ "$FORMAT_UPSTREAM" ] || FORMAT_UPSTREAM="$FORMAT_REMOTE/$(LC_ALL=POSIX LANG=POSIX git remote show $FORMAT_REMOTE | sed -n '/HEAD branch/s/.*: //p')"
|
||||
# Get the common ancestor with that remote branch. Everything after that
|
||||
# common ancestor would be considered the contents of a pull request, so
|
||||
# should be relevant for formatting fixes.
|
||||
FORMAT_UPSTREAM_MERGE_BASE="$(git merge-base "$FORMAT_UPSTREAM" HEAD)"
|
||||
# Get the differences
|
||||
diffs=$(git diff -U0 "$FORMAT_UPSTREAM_MERGE_BASE" -- $EXCLUDE | $CLANG_FORMAT_DIFF -p 1) || true
|
||||
echo "Checking format of changes not yet in $FORMAT_UPSTREAM..."
|
||||
# Check the format of last commit
|
||||
diffs=$(git diff -U0 $LAST_MASTER^ | $CLANG_FORMAT_DIFF -p 1)
|
||||
else
|
||||
# Check the format of uncommitted lines,
|
||||
diffs=$(git diff -U0 HEAD -- $EXCLUDE | $CLANG_FORMAT_DIFF -p 1) || true
|
||||
echo "Checking format of uncommitted changes..."
|
||||
fi
|
||||
|
||||
# Check for missing copyright in new files
|
||||
echo "Checking for copyright headers in new files..."
|
||||
|
||||
# Get list of new files (added, not just modified)
|
||||
if [ -z "$uncommitted_code" ]; then
|
||||
# Post-commit: check files added since merge base
|
||||
new_files=$(git diff --name-only --diff-filter=A "$FORMAT_UPSTREAM_MERGE_BASE" -- '*.h' '*.cc' '*.py' $EXCLUDE)
|
||||
else
|
||||
# Pre-commit: check staged new files
|
||||
new_files=$(git diff --name-only --diff-filter=A --cached HEAD -- '*.h' '*.cc' '*.py' $EXCLUDE)
|
||||
fi
|
||||
|
||||
if [ -n "$new_files" ]; then
|
||||
files_missing_copyright=""
|
||||
|
||||
for file in $new_files; do
|
||||
if [ -f "$file" ]; then
|
||||
# Check if file is missing copyright
|
||||
# For .py files, check for Python-style comment
|
||||
# For .h and .cc files, check for C++-style comment
|
||||
if [[ "$file" == *.py ]]; then
|
||||
if ! grep -q "Copyright (c) Meta Platforms, Inc. and affiliates" "$file"; then
|
||||
files_missing_copyright="$files_missing_copyright $file"
|
||||
# Add copyright header to Python file
|
||||
temp_file=$(mktemp)
|
||||
{
|
||||
echo "# Copyright (c) Meta Platforms, Inc. and affiliates."
|
||||
echo "# This source code is licensed under both the GPLv2 (found in the COPYING file in the root directory)"
|
||||
echo "# and the Apache 2.0 License (found in the LICENSE.Apache file in the root directory)."
|
||||
echo
|
||||
cat "$file"
|
||||
} > "$temp_file"
|
||||
mv "$temp_file" "$file"
|
||||
echo "Added copyright header to $file"
|
||||
fi
|
||||
elif [[ "$file" == *.h ]] || [[ "$file" == *.cc ]]; then
|
||||
if ! grep -q "Copyright (c) Meta Platforms, Inc. and affiliates" "$file"; then
|
||||
files_missing_copyright="$files_missing_copyright $file"
|
||||
# Add copyright header to C++ file
|
||||
temp_file=$(mktemp)
|
||||
{
|
||||
echo "// Copyright (c) Meta Platforms, Inc. and affiliates. "
|
||||
echo "// This source code is licensed under both the GPLv2 (found in the "
|
||||
echo "// COPYING file in the root directory) and Apache 2.0 License "
|
||||
echo "// (found in the LICENSE.Apache file in the root directory)."
|
||||
echo
|
||||
cat "$file"
|
||||
} > "$temp_file"
|
||||
mv "$temp_file" "$file"
|
||||
echo "Added copyright header to $file"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
if [ -n "$files_missing_copyright" ]; then
|
||||
echo "Copyright headers were added to new files."
|
||||
else
|
||||
echo "All new files have copyright headers."
|
||||
fi
|
||||
else
|
||||
echo "No new files to check for copyright headers."
|
||||
diffs=$(git diff -U0 HEAD | $CLANG_FORMAT_DIFF -p 1)
|
||||
fi
|
||||
|
||||
if [ -z "$diffs" ]
|
||||
then
|
||||
echo "Nothing needs to be reformatted!"
|
||||
exit 0
|
||||
elif [ $? -ne 1 ]; then
|
||||
# CLANG_FORMAT_DIFF will exit on 1 while there is suggested changes.
|
||||
exit $?
|
||||
elif [ $CHECK_ONLY ]
|
||||
then
|
||||
echo "Your change has unformatted code. Please run make format!"
|
||||
if [ $VERBOSE_CHECK ]; then
|
||||
clang-format --version
|
||||
echo "$diffs"
|
||||
fi
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Highlight the insertion/deletion from the clang-format-diff.py's output
|
||||
COLOR_END="\033[0m"
|
||||
COLOR_RED="\033[0;31m"
|
||||
COLOR_GREEN="\033[0;32m"
|
||||
COLOR_RED="\033[0;31m"
|
||||
COLOR_GREEN="\033[0;32m"
|
||||
|
||||
echo -e "Detect lines that doesn't follow the format rules:\r"
|
||||
# Add the color to the diff. lines added will be green; lines removed will be red.
|
||||
echo "$diffs" |
|
||||
echo "$diffs" |
|
||||
sed -e "s/\(^-.*$\)/`echo -e \"$COLOR_RED\1$COLOR_END\"`/" |
|
||||
sed -e "s/\(^+.*$\)/`echo -e \"$COLOR_GREEN\1$COLOR_END\"`/"
|
||||
|
||||
# Handle auto-apply mode (non-interactive)
|
||||
if [ "$AUTO_APPLY" ]; then
|
||||
to_fix="y"
|
||||
else
|
||||
echo -e "Would you like to fix the format automatically (y/n): \c"
|
||||
|
||||
# Make sure under any mode, we can read user input.
|
||||
exec < /dev/tty
|
||||
read to_fix
|
||||
if [[ "$OPT" == *"-DTRAVIS"* ]]
|
||||
then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo -e "Would you like to fix the format automatically (y/n): \c"
|
||||
|
||||
# Make sure under any mode, we can read user input.
|
||||
exec < /dev/tty
|
||||
read to_fix
|
||||
|
||||
if [ "$to_fix" != "y" ]
|
||||
then
|
||||
exit 1
|
||||
@@ -263,15 +104,14 @@ fi
|
||||
# Do in-place format adjustment.
|
||||
if [ -z "$uncommitted_code" ]
|
||||
then
|
||||
git diff -U0 "$FORMAT_UPSTREAM_MERGE_BASE" -- $EXCLUDE | $CLANG_FORMAT_DIFF -i -p 1
|
||||
git diff -U0 $LAST_MASTER^ | $CLANG_FORMAT_DIFF -i -p 1
|
||||
else
|
||||
git diff -U0 HEAD -- $EXCLUDE | $CLANG_FORMAT_DIFF -i -p 1
|
||||
git diff -U0 HEAD^ | $CLANG_FORMAT_DIFF -i -p 1
|
||||
fi
|
||||
echo "Files reformatted!"
|
||||
|
||||
# Amend to last commit if user do the post-commit format check
|
||||
# Skip amend prompt in auto-apply mode (user can amend manually if desired)
|
||||
if [ -z "$uncommitted_code" ] && [ -z "$AUTO_APPLY" ]; then
|
||||
if [ -z "$uncommitted_code" ]; then
|
||||
echo -e "Would you like to amend the changes to last commit (`git log HEAD --oneline | head -1`)? (y/n): \c"
|
||||
read to_amend
|
||||
|
||||
|
||||
@@ -1,228 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
# This source code is licensed under both the GPLv2 (found in the COPYING file in the root directory)
|
||||
# and the Apache 2.0 License (found in the LICENSE.Apache file in the root directory).
|
||||
"""
|
||||
Pre-download packages with unreliable mirrors using fallback mirrors.
|
||||
Reads package info from folly's getdeps manifest files.
|
||||
"""
|
||||
import configparser
|
||||
import hashlib
|
||||
import os
|
||||
import shutil
|
||||
import sys
|
||||
import urllib.request
|
||||
|
||||
DOWNLOAD_TIMEOUT_SECONDS = 120
|
||||
DOWNLOAD_CHUNK_BYTES = 64 * 1024
|
||||
MAX_DOWNLOAD_BYTES = 50 * 1024 * 1024
|
||||
|
||||
MIRROR_FALLBACKS = {
|
||||
"ftpmirror.gnu.org/gnu/": [
|
||||
"https://mirrors.kernel.org/gnu/",
|
||||
"https://ftpmirror.gnu.org/gnu/",
|
||||
"https://ftp.gnu.org/gnu/",
|
||||
],
|
||||
"ftp.gnu.org/gnu/": [
|
||||
"https://mirrors.kernel.org/gnu/",
|
||||
"https://ftpmirror.gnu.org/gnu/",
|
||||
"https://ftp.gnu.org/gnu/",
|
||||
],
|
||||
}
|
||||
|
||||
# These packages must have URLs matching MIRROR_FALLBACKS; other packages are
|
||||
# left for getdeps.py's normal download path.
|
||||
PACKAGES_TO_CHECK = ("autoconf", "automake", "libtool", "libiberty")
|
||||
|
||||
|
||||
def sha256_file(path):
|
||||
"""Calculate SHA256 hash of a file."""
|
||||
h = hashlib.sha256()
|
||||
try:
|
||||
with open(path, "rb") as f:
|
||||
for chunk in iter(lambda: f.read(65536), b""):
|
||||
h.update(chunk)
|
||||
return h.hexdigest()
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
|
||||
def parse_manifest(manifest_path):
|
||||
"""Parse a getdeps manifest file to extract download info."""
|
||||
# folly manifests can contain bare keys in sections unrelated to downloads.
|
||||
config = configparser.ConfigParser(allow_no_value=True, interpolation=None)
|
||||
try:
|
||||
with open(manifest_path, encoding="utf-8") as manifest_file:
|
||||
config.read_file(manifest_file)
|
||||
except Exception as ex:
|
||||
print(f" {os.path.basename(manifest_path)}: WARNING - parse failed: {ex}")
|
||||
return None
|
||||
|
||||
if "download" in config:
|
||||
return {
|
||||
"url": config["download"].get("url", ""),
|
||||
"sha256": config["download"].get("sha256", ""),
|
||||
}
|
||||
return None
|
||||
|
||||
|
||||
def file_size(path):
|
||||
try:
|
||||
return os.path.getsize(path)
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
|
||||
def get_fallback_mirrors(url):
|
||||
"""Get fallback mirror URLs for a given URL."""
|
||||
for pattern, mirrors in MIRROR_FALLBACKS.items():
|
||||
if pattern in url:
|
||||
# Extract the path after the pattern
|
||||
path_start = url.find(pattern) + len(pattern)
|
||||
path = url[path_start:]
|
||||
return [mirror + path for mirror in mirrors]
|
||||
return []
|
||||
|
||||
|
||||
def download_url(url, filepath):
|
||||
"""Download URL to filepath without leaving partial files behind."""
|
||||
tmp_filepath = filepath + ".tmp"
|
||||
if os.path.exists(tmp_filepath):
|
||||
os.remove(tmp_filepath)
|
||||
|
||||
request = urllib.request.Request(
|
||||
url, headers={"User-Agent": "rocksdb-getdeps-fallback/1.0"}
|
||||
)
|
||||
try:
|
||||
with urllib.request.urlopen(
|
||||
request, timeout=DOWNLOAD_TIMEOUT_SECONDS
|
||||
) as response, open(tmp_filepath, "wb") as output:
|
||||
copied = 0
|
||||
while True:
|
||||
chunk = response.read(DOWNLOAD_CHUNK_BYTES)
|
||||
if not chunk:
|
||||
break
|
||||
|
||||
copied += len(chunk)
|
||||
if copied > MAX_DOWNLOAD_BYTES:
|
||||
raise Exception(
|
||||
f"download exceeds {MAX_DOWNLOAD_BYTES} bytes"
|
||||
)
|
||||
output.write(chunk)
|
||||
os.replace(tmp_filepath, filepath)
|
||||
finally:
|
||||
if os.path.exists(tmp_filepath):
|
||||
os.remove(tmp_filepath)
|
||||
|
||||
|
||||
def prepare_download(package, info, download_dir, cache_dir):
|
||||
url = info["url"]
|
||||
expected_sha256 = info["sha256"]
|
||||
mirrors = get_fallback_mirrors(url)
|
||||
if not mirrors:
|
||||
return False
|
||||
|
||||
if not expected_sha256:
|
||||
print(f" {package}: WARNING - skipped fallback without sha256")
|
||||
return False
|
||||
|
||||
# getdeps uses format: {package}-{filename}
|
||||
filename = f"{package}-{os.path.basename(url)}"
|
||||
filepath = os.path.join(download_dir, filename)
|
||||
cache_path = os.path.join(cache_dir, filename)
|
||||
|
||||
# Check if already valid.
|
||||
actual_sha256 = sha256_file(filepath) if os.path.exists(filepath) else None
|
||||
if actual_sha256 == expected_sha256:
|
||||
print(f" {filename}: OK (already downloaded)")
|
||||
return True
|
||||
if actual_sha256 is not None:
|
||||
print(
|
||||
f" {filename}: WARNING - removing invalid download "
|
||||
f"sha256={actual_sha256}"
|
||||
)
|
||||
os.remove(filepath)
|
||||
|
||||
# The cache is only an opportunistic single-build accelerator; callers
|
||||
# should not share it across concurrent builds without external locking.
|
||||
actual_sha256 = sha256_file(cache_path) if os.path.exists(cache_path) else None
|
||||
if actual_sha256 == expected_sha256:
|
||||
print(f" {filename}: OK (from cache)")
|
||||
shutil.copy2(cache_path, filepath)
|
||||
return True
|
||||
if actual_sha256 is not None:
|
||||
print(
|
||||
f" {filename}: WARNING - removing invalid cache "
|
||||
f"sha256={actual_sha256}"
|
||||
)
|
||||
os.remove(cache_path)
|
||||
|
||||
# Try fallback mirrors.
|
||||
for mirror_url in mirrors:
|
||||
print(f" {filename}: trying {mirror_url}...")
|
||||
try:
|
||||
download_url(mirror_url, filepath)
|
||||
except Exception as ex:
|
||||
print(f" {filename}: WARNING - download failed: {ex}")
|
||||
continue
|
||||
|
||||
actual_sha256 = sha256_file(filepath)
|
||||
if actual_sha256 == expected_sha256:
|
||||
size = file_size(filepath)
|
||||
print(f" {filename}: OK (downloaded, {size} bytes)")
|
||||
shutil.copy2(filepath, cache_path)
|
||||
return True
|
||||
|
||||
size = file_size(filepath)
|
||||
print(
|
||||
f" {filename}: WARNING - sha256 mismatch from {mirror_url}: "
|
||||
f"expected={expected_sha256} actual={actual_sha256} size={size}"
|
||||
)
|
||||
os.remove(filepath)
|
||||
|
||||
print(f" {filename}: WARNING - all mirrors failed")
|
||||
return False
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) != 4:
|
||||
print(f"Usage: {sys.argv[0]} <download_dir> <cache_dir> <manifests_dir>")
|
||||
sys.exit(1)
|
||||
|
||||
download_dir, cache_dir, manifests_dir = sys.argv[1], sys.argv[2], sys.argv[3]
|
||||
os.makedirs(download_dir, exist_ok=True)
|
||||
os.makedirs(cache_dir, exist_ok=True)
|
||||
|
||||
checked = 0
|
||||
ready = 0
|
||||
for package in PACKAGES_TO_CHECK:
|
||||
manifest_path = os.path.join(manifests_dir, package)
|
||||
if not os.path.isfile(manifest_path):
|
||||
continue
|
||||
|
||||
info = parse_manifest(manifest_path)
|
||||
if not info or not info["url"]:
|
||||
continue
|
||||
|
||||
if not info["sha256"]:
|
||||
print(f" {package}: WARNING - skipped fallback without sha256")
|
||||
continue
|
||||
|
||||
if not get_fallback_mirrors(info["url"]):
|
||||
print(
|
||||
f" {package}: WARNING - skipped fallback without known mirror "
|
||||
f"for {info['url']}"
|
||||
)
|
||||
continue
|
||||
|
||||
checked += 1
|
||||
try:
|
||||
if prepare_download(package, info, download_dir, cache_dir):
|
||||
ready += 1
|
||||
except Exception as ex:
|
||||
print(f" {package}: WARNING - fallback preparation failed: {ex}")
|
||||
|
||||
print(f" fallback mirror downloads ready: {ready}/{checked}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
+10
-45
@@ -1170,7 +1170,7 @@ sub parse_env_var {
|
||||
# Check if any variables contain \n
|
||||
if(my @v = map { s/BASH_FUNC_(.*)\(\)/$1/; $_ } grep { $ENV{$_}=~/\n/ } @vars) {
|
||||
# \n is bad for csh and will cause it to fail.
|
||||
$Global::envwarn = ::shell_quote_scalar(q{echo $SHELL | grep -E "/t?csh" > /dev/null && echo CSH/TCSH DO NOT SUPPORT newlines IN VARIABLES/FUNCTIONS. Unset }."@v".q{ && exec false;}."\n\n") . $Global::envwarn;
|
||||
$Global::envwarn = ::shell_quote_scalar(q{echo $SHELL | egrep "/t?csh" > /dev/null && echo CSH/TCSH DO NOT SUPPORT newlines IN VARIABLES/FUNCTIONS. Unset }."@v".q{ && exec false;}."\n\n") . $Global::envwarn;
|
||||
}
|
||||
|
||||
if(not @qcsh) { push @qcsh, "true"; }
|
||||
@@ -1561,7 +1561,6 @@ sub save_stdin_stdout_stderr {
|
||||
::die_bug("Can't dup STDERR: $!");
|
||||
open $Global::original_stdin, "<&", "STDIN" or
|
||||
::die_bug("Can't dup STDIN: $!");
|
||||
$Global::is_terminal = (-t $Global::original_stderr) && !$ENV{'CIRCLECI'}&& !$ENV{'GITHUB_ACTIONS'} && !$ENV{'TRAVIS'};
|
||||
}
|
||||
|
||||
sub enough_file_handles {
|
||||
@@ -1841,17 +1840,12 @@ sub start_another_job {
|
||||
}
|
||||
}
|
||||
|
||||
$opt::min_progress_interval = 0;
|
||||
|
||||
sub init_progress {
|
||||
# Uses:
|
||||
# $opt::bar
|
||||
# Returns:
|
||||
# list of computers for progress output
|
||||
$|=1;
|
||||
if (not $Global::is_terminal) {
|
||||
$opt::min_progress_interval = 30;
|
||||
}
|
||||
if($opt::bar) {
|
||||
return("","");
|
||||
}
|
||||
@@ -1876,9 +1870,6 @@ sub drain_job_queue {
|
||||
}
|
||||
my $last_header="";
|
||||
my $sleep = 0.2;
|
||||
my $last_left = 1000000000;
|
||||
my $last_progress_time = 0;
|
||||
my $ps_reported = 0;
|
||||
do {
|
||||
while($Global::total_running > 0) {
|
||||
debug($Global::total_running, "==", scalar
|
||||
@@ -1889,39 +1880,14 @@ sub drain_job_queue {
|
||||
close $job->fh(0,"w");
|
||||
}
|
||||
}
|
||||
# When not connected to terminal, assume CI (e.g. CircleCI). In
|
||||
# that case we want occasional progress output to prevent abort
|
||||
# due to timeout with no output, but we also need to stop sending
|
||||
# progress output if there has been no actual progress, so that
|
||||
# the job can time out appropriately (CirecleCI: 10m) in case of
|
||||
# a hung test. But without special output, it is extremely
|
||||
# annoying to diagnose which test is hung, so we add that using
|
||||
# `ps` below.
|
||||
if($opt::progress and
|
||||
($Global::is_terminal or (time() - $last_progress_time) >= 30)) {
|
||||
if($opt::progress) {
|
||||
my %progress = progress();
|
||||
if($last_header ne $progress{'header'}) {
|
||||
print $Global::original_stderr "\n", $progress{'header'}, "\n";
|
||||
$last_header = $progress{'header'};
|
||||
}
|
||||
if ($Global::is_terminal) {
|
||||
print $Global::original_stderr "\r",$progress{'status'};
|
||||
}
|
||||
if ($last_left > $Global::left) {
|
||||
if (not $Global::is_terminal) {
|
||||
print $Global::original_stderr $progress{'status'},"\n";
|
||||
}
|
||||
$last_progress_time = time();
|
||||
$ps_reported = 0;
|
||||
} elsif (not $ps_reported and (time() - $last_progress_time) >= 300) {
|
||||
# No progress in at least 60 seconds: run ps
|
||||
print $Global::original_stderr "\n";
|
||||
my $script_dir = ::dirname($0);
|
||||
system("$script_dir/ps_with_stack || ps -wwf");
|
||||
$ps_reported = 1;
|
||||
}
|
||||
$last_left = $Global::left;
|
||||
flush $Global::original_stderr;
|
||||
print $Global::original_stderr "\r",$progress{'status'};
|
||||
flush $Global::original_stderr;
|
||||
}
|
||||
if($Global::total_running < $Global::max_jobs_running
|
||||
and not $Global::JobQueue->empty()) {
|
||||
@@ -1955,7 +1921,7 @@ sub drain_job_queue {
|
||||
not $Global::start_no_new_jobs and not $Global::JobQueue->empty());
|
||||
if($opt::progress) {
|
||||
my %progress = progress();
|
||||
print $Global::original_stderr $opt::progress_sep, $progress{'status'}, "\n";
|
||||
print $Global::original_stderr "\r", $progress{'status'}, "\n";
|
||||
flush $Global::original_stderr;
|
||||
}
|
||||
}
|
||||
@@ -1988,11 +1954,10 @@ sub progress {
|
||||
my $eta = "";
|
||||
my ($status,$header)=("","");
|
||||
if($opt::eta) {
|
||||
my($total, $completed, $left, $pctcomplete, $avgtime, $this_eta) =
|
||||
compute_eta();
|
||||
$eta = sprintf("ETA: %ds Left: %d AVG: %.2fs ",
|
||||
$this_eta, $left, $avgtime);
|
||||
$Global::left = $left;
|
||||
my($total, $completed, $left, $pctcomplete, $avgtime, $this_eta) =
|
||||
compute_eta();
|
||||
$eta = sprintf("ETA: %ds Left: %d AVG: %.2fs ",
|
||||
$this_eta, $left, $avgtime);
|
||||
}
|
||||
my $termcols = terminal_columns();
|
||||
my @workers = sort keys %Global::host;
|
||||
@@ -5836,7 +5801,7 @@ sub workdir {
|
||||
. "-" . $self->seq();
|
||||
} else {
|
||||
$workdir = $opt::workdir;
|
||||
# Rsync treats /./ special. We don't want that
|
||||
# Rsync treats /./ special. We dont want that
|
||||
$workdir =~ s:/\./:/:g; # Remove /./
|
||||
$workdir =~ s:/+$::; # Remove ending / if any
|
||||
$workdir =~ s:^\./::g; # Remove starting ./ if any
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# Copyright 2017 Google Inc. All rights reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
import gtest_parallel
|
||||
import sys
|
||||
|
||||
sys.exit(gtest_parallel.main())
|
||||
@@ -1,959 +0,0 @@
|
||||
# Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
# This source code is licensed under both the GPLv2 (found in the COPYING file in the root directory)
|
||||
# and the Apache 2.0 License (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
# Copyright 2013 Google Inc. All rights reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
import errno
|
||||
from functools import total_ordering
|
||||
import gzip
|
||||
import io
|
||||
import json
|
||||
import multiprocessing
|
||||
import optparse
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import signal
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import threading
|
||||
import time
|
||||
|
||||
if sys.version_info.major >= 3:
|
||||
long = int
|
||||
import _pickle as cPickle
|
||||
import _thread as thread
|
||||
else:
|
||||
import cPickle
|
||||
import thread
|
||||
|
||||
from pickle import HIGHEST_PROTOCOL as PICKLE_HIGHEST_PROTOCOL
|
||||
|
||||
if sys.platform == 'win32':
|
||||
import msvcrt
|
||||
else:
|
||||
import fcntl
|
||||
|
||||
|
||||
# An object that catches SIGINT sent to the Python process and notices
|
||||
# if processes passed to wait() die by SIGINT (we need to look for
|
||||
# both of those cases, because pressing Ctrl+C can result in either
|
||||
# the main process or one of the subprocesses getting the signal).
|
||||
#
|
||||
# Before a SIGINT is seen, wait(p) will simply call p.wait() and
|
||||
# return the result. Once a SIGINT has been seen (in the main process
|
||||
# or a subprocess, including the one the current call is waiting for),
|
||||
# wait(p) will call p.terminate() and raise ProcessWasInterrupted.
|
||||
class SigintHandler(object):
|
||||
class ProcessWasInterrupted(Exception):
|
||||
pass
|
||||
|
||||
sigint_returncodes = {
|
||||
-signal.SIGINT, # Unix
|
||||
-1073741510, # Windows
|
||||
}
|
||||
|
||||
def __init__(self):
|
||||
self.__lock = threading.Lock()
|
||||
self.__processes = set()
|
||||
self.__got_sigint = False
|
||||
signal.signal(signal.SIGINT, lambda signal_num, frame: self.interrupt())
|
||||
|
||||
def __on_sigint(self):
|
||||
self.__got_sigint = True
|
||||
while self.__processes:
|
||||
try:
|
||||
self.__processes.pop().terminate()
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
def interrupt(self):
|
||||
with self.__lock:
|
||||
self.__on_sigint()
|
||||
|
||||
def got_sigint(self):
|
||||
with self.__lock:
|
||||
return self.__got_sigint
|
||||
|
||||
def wait(self, p, timeout_per_test):
|
||||
with self.__lock:
|
||||
if self.__got_sigint:
|
||||
p.terminate()
|
||||
self.__processes.add(p)
|
||||
try:
|
||||
code = p.wait(timeout_per_test)
|
||||
except subprocess.TimeoutExpired :
|
||||
p.terminate()
|
||||
self.__processes.remove(p)
|
||||
code = -errno.ETIME
|
||||
with self.__lock:
|
||||
self.__processes.discard(p)
|
||||
if code in self.sigint_returncodes:
|
||||
self.__on_sigint()
|
||||
if self.__got_sigint:
|
||||
raise self.ProcessWasInterrupted
|
||||
return code
|
||||
|
||||
|
||||
sigint_handler = SigintHandler()
|
||||
|
||||
|
||||
# Return the width of the terminal, or None if it couldn't be
|
||||
# determined (e.g. because we're not being run interactively).
|
||||
def term_width(out):
|
||||
if not out.isatty():
|
||||
return None
|
||||
try:
|
||||
p = subprocess.Popen(["stty", "size"],
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE)
|
||||
(out, err) = p.communicate()
|
||||
if p.returncode != 0 or err:
|
||||
return None
|
||||
return int(out.split()[1])
|
||||
except (IndexError, OSError, ValueError):
|
||||
return None
|
||||
|
||||
|
||||
# Output transient and permanent lines of text. If several transient
|
||||
# lines are written in sequence, the new will overwrite the old. We
|
||||
# use this to ensure that lots of unimportant info (tests passing)
|
||||
# won't drown out important info (tests failing).
|
||||
class Outputter(object):
|
||||
def __init__(self, out_file):
|
||||
self.__out_file = out_file
|
||||
self.__previous_line_was_transient = False
|
||||
self.__width = term_width(out_file) # Line width, or None if not a tty.
|
||||
|
||||
def transient_line(self, msg):
|
||||
if self.__width is None:
|
||||
self.__out_file.write(msg + "\n")
|
||||
self.__out_file.flush()
|
||||
else:
|
||||
self.__out_file.write("\r" + msg[:self.__width].ljust(self.__width))
|
||||
self.__previous_line_was_transient = True
|
||||
|
||||
def flush_transient_output(self):
|
||||
if self.__previous_line_was_transient:
|
||||
self.__out_file.write("\n")
|
||||
self.__previous_line_was_transient = False
|
||||
|
||||
def permanent_line(self, msg):
|
||||
self.flush_transient_output()
|
||||
self.__out_file.write(msg + "\n")
|
||||
if self.__width is None:
|
||||
self.__out_file.flush()
|
||||
|
||||
|
||||
def get_save_file_path():
|
||||
"""Return path to file for saving transient data."""
|
||||
if sys.platform == 'win32':
|
||||
default_cache_path = os.path.join(os.path.expanduser('~'), 'AppData',
|
||||
'Local')
|
||||
cache_path = os.environ.get('LOCALAPPDATA', default_cache_path)
|
||||
else:
|
||||
# We don't use xdg module since it's not a standard.
|
||||
default_cache_path = os.path.join(os.path.expanduser('~'), '.cache')
|
||||
cache_path = os.environ.get('XDG_CACHE_HOME', default_cache_path)
|
||||
|
||||
if os.path.isdir(cache_path):
|
||||
return os.path.join(cache_path, 'gtest-parallel')
|
||||
else:
|
||||
sys.stderr.write('Directory {} does not exist'.format(cache_path))
|
||||
return os.path.join(os.path.expanduser('~'), '.gtest-parallel-times')
|
||||
|
||||
|
||||
@total_ordering
|
||||
class Task(object):
|
||||
"""Stores information about a task (single execution of a test).
|
||||
|
||||
This class stores information about the test to be executed (gtest binary and
|
||||
test name), and its result (log file, exit code and runtime).
|
||||
Each task is uniquely identified by the gtest binary, the test name and an
|
||||
execution number that increases each time the test is executed.
|
||||
Additionaly we store the last execution time, so that next time the test is
|
||||
executed, the slowest tests are run first.
|
||||
"""
|
||||
|
||||
def __init__(self, test_binary, test_name, test_command, execution_number,
|
||||
last_execution_time, output_dir):
|
||||
self.test_name = test_name
|
||||
self.output_dir = output_dir
|
||||
self.test_binary = test_binary
|
||||
self.test_command = test_command
|
||||
self.execution_number = execution_number
|
||||
self.last_execution_time = last_execution_time
|
||||
|
||||
self.exit_code = None
|
||||
self.runtime_ms = None
|
||||
|
||||
self.test_id = (test_binary, test_name)
|
||||
self.task_id = (test_binary, test_name, self.execution_number)
|
||||
|
||||
self.log_file = Task._logname(self.output_dir, self.test_binary, test_name,
|
||||
self.execution_number)
|
||||
|
||||
def __sorting_key(self):
|
||||
# Unseen or failing tests (both missing execution time) take precedence over
|
||||
# execution time. Tests are greater (seen as slower) when missing times so
|
||||
# that they are executed first.
|
||||
return (1 if self.last_execution_time is None else 0,
|
||||
self.last_execution_time)
|
||||
|
||||
def __eq__(self, other):
|
||||
return self.__sorting_key() == other.__sorting_key()
|
||||
|
||||
def __ne__(self, other):
|
||||
return not (self == other)
|
||||
|
||||
def __lt__(self, other):
|
||||
return self.__sorting_key() < other.__sorting_key()
|
||||
|
||||
@staticmethod
|
||||
def _normalize(string):
|
||||
return re.sub('[^A-Za-z0-9]', '_', string)
|
||||
|
||||
@staticmethod
|
||||
def _logname(output_dir, test_binary, test_name, execution_number):
|
||||
# Store logs to temporary files if there is no output_dir.
|
||||
if output_dir is None:
|
||||
(log_handle, log_name) = tempfile.mkstemp(prefix='gtest_parallel_',
|
||||
suffix=".log")
|
||||
os.close(log_handle)
|
||||
return log_name
|
||||
|
||||
log_name = '%s-%s-%d.log' % (Task._normalize(os.path.basename(test_binary)),
|
||||
Task._normalize(test_name), execution_number)
|
||||
|
||||
return os.path.join(output_dir, log_name)
|
||||
|
||||
def run(self, timeout_per_test):
|
||||
begin = time.time()
|
||||
with open(self.log_file, 'w') as log:
|
||||
task = subprocess.Popen(self.test_command, stdout=log, stderr=log)
|
||||
try:
|
||||
self.exit_code = sigint_handler.wait(task, timeout_per_test)
|
||||
except sigint_handler.ProcessWasInterrupted:
|
||||
thread.exit()
|
||||
self.runtime_ms = int(1000 * (time.time() - begin))
|
||||
self.last_execution_time = None if self.exit_code else self.runtime_ms
|
||||
|
||||
|
||||
class TaskManager(object):
|
||||
"""Executes the tasks and stores the passed, failed and interrupted tasks.
|
||||
|
||||
When a task is run, this class keeps track if it passed, failed or was
|
||||
interrupted. After a task finishes it calls the relevant functions of the
|
||||
Logger, TestResults and TestTimes classes, and in case of failure, retries the
|
||||
test as specified by the --retry_failed flag.
|
||||
"""
|
||||
|
||||
def __init__(self, times, logger, test_results, task_factory, times_to_retry,
|
||||
initial_execution_number):
|
||||
self.times = times
|
||||
self.logger = logger
|
||||
self.test_results = test_results
|
||||
self.task_factory = task_factory
|
||||
self.times_to_retry = times_to_retry
|
||||
self.initial_execution_number = initial_execution_number
|
||||
|
||||
self.global_exit_code = 0
|
||||
|
||||
self.passed = []
|
||||
self.failed = []
|
||||
self.started = {}
|
||||
self.timed_out = []
|
||||
self.execution_number = {}
|
||||
|
||||
self.lock = threading.Lock()
|
||||
|
||||
def __get_next_execution_number(self, test_id):
|
||||
with self.lock:
|
||||
next_execution_number = self.execution_number.setdefault(
|
||||
test_id, self.initial_execution_number)
|
||||
self.execution_number[test_id] += 1
|
||||
return next_execution_number
|
||||
|
||||
def __register_start(self, task):
|
||||
with self.lock:
|
||||
self.started[task.task_id] = task
|
||||
|
||||
def register_exit(self, task):
|
||||
self.logger.log_exit(task)
|
||||
self.times.record_test_time(task.test_binary, task.test_name,
|
||||
task.last_execution_time)
|
||||
if self.test_results:
|
||||
self.test_results.log(task.test_name, task.runtime_ms / 1000.0,
|
||||
task.exit_code)
|
||||
|
||||
with self.lock:
|
||||
self.started.pop(task.task_id)
|
||||
if task.exit_code == 0:
|
||||
self.passed.append(task)
|
||||
elif task.exit_code == -errno.ETIME:
|
||||
self.timed_out.append(task)
|
||||
else:
|
||||
self.failed.append(task)
|
||||
|
||||
def run_task(self, task, timeout_per_test):
|
||||
for try_number in range(self.times_to_retry + 1):
|
||||
self.__register_start(task)
|
||||
task.run(timeout_per_test)
|
||||
self.register_exit(task)
|
||||
|
||||
if task.exit_code == 0:
|
||||
break
|
||||
|
||||
if try_number < self.times_to_retry:
|
||||
execution_number = self.__get_next_execution_number(task.test_id)
|
||||
# We need create a new Task instance. Each task represents a single test
|
||||
# execution, with its own runtime, exit code and log file.
|
||||
task = self.task_factory(task.test_binary, task.test_name,
|
||||
task.test_command, execution_number,
|
||||
task.last_execution_time, task.output_dir)
|
||||
|
||||
with self.lock:
|
||||
if task.exit_code != 0:
|
||||
self.global_exit_code = task.exit_code
|
||||
|
||||
|
||||
class FilterFormat(object):
|
||||
def __init__(self, output_dir):
|
||||
if sys.stdout.isatty():
|
||||
# stdout needs to be unbuffered since the output is interactive.
|
||||
if isinstance(sys.stdout, io.TextIOWrapper):
|
||||
# workaround for https://bugs.python.org/issue17404
|
||||
sys.stdout = io.TextIOWrapper(sys.stdout.detach(),
|
||||
line_buffering=True,
|
||||
write_through=True,
|
||||
newline='\n')
|
||||
else:
|
||||
sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)
|
||||
|
||||
self.output_dir = output_dir
|
||||
|
||||
self.total_tasks = 0
|
||||
self.finished_tasks = 0
|
||||
self.out = Outputter(sys.stdout)
|
||||
self.stdout_lock = threading.Lock()
|
||||
|
||||
def move_to(self, destination_dir, tasks):
|
||||
if self.output_dir is None:
|
||||
return
|
||||
|
||||
destination_dir = os.path.join(self.output_dir, destination_dir)
|
||||
os.makedirs(destination_dir)
|
||||
for task in tasks:
|
||||
shutil.move(task.log_file, destination_dir)
|
||||
|
||||
def print_tests(self, message, tasks, print_try_number, print_test_command):
|
||||
self.out.permanent_line("%s (%s/%s):" %
|
||||
(message, len(tasks), self.total_tasks))
|
||||
for task in sorted(tasks):
|
||||
runtime_ms = 'Interrupted'
|
||||
if task.runtime_ms is not None:
|
||||
runtime_ms = '%d ms' % task.runtime_ms
|
||||
if print_test_command:
|
||||
try:
|
||||
cmd_str = " ".join(task.test_command)
|
||||
except TypeError:
|
||||
cmd_str = task.test_command
|
||||
self.out.permanent_line(
|
||||
"%11s: %s%s" %
|
||||
(runtime_ms, cmd_str,
|
||||
(" (try #%d)" % task.execution_number) if print_try_number else ""))
|
||||
else:
|
||||
self.out.permanent_line(
|
||||
"%11s: %s %s%s" %
|
||||
(runtime_ms, task.test_binary, task.test_name,
|
||||
(" (try #%d)" % task.execution_number) if print_try_number else ""))
|
||||
|
||||
def log_exit(self, task):
|
||||
with self.stdout_lock:
|
||||
self.finished_tasks += 1
|
||||
self.out.transient_line("[%d/%d] %s (%d ms)" %
|
||||
(self.finished_tasks, self.total_tasks,
|
||||
task.test_name, task.runtime_ms))
|
||||
if task.exit_code != 0:
|
||||
signal_name = None
|
||||
if task.exit_code < 0:
|
||||
try:
|
||||
signal_name = signal.Signals(-task.exit_code).name
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
with open(task.log_file) as f:
|
||||
for line in f.readlines():
|
||||
self.out.permanent_line(line.rstrip())
|
||||
if task.exit_code is None:
|
||||
self.out.permanent_line("[%d/%d] %s aborted after %d ms" %
|
||||
(self.finished_tasks, self.total_tasks,
|
||||
task.test_name, task.runtime_ms))
|
||||
elif task.exit_code == -errno.ETIME:
|
||||
self.out.permanent_line(
|
||||
"\033[31m[ TIMEOUT ]\033[0m %s timed out after %d s"
|
||||
% (task.test_name, task.runtime_ms/1000))
|
||||
elif signal_name is not None:
|
||||
self.out.permanent_line(
|
||||
"[%d/%d] %s killed by signal %s (%d ms)" %
|
||||
(self.finished_tasks, self.total_tasks, task.test_name,
|
||||
signal_name, task.runtime_ms))
|
||||
else:
|
||||
self.out.permanent_line(
|
||||
"[%d/%d] %s returned with exit code %d (%d ms)" %
|
||||
(self.finished_tasks, self.total_tasks, task.test_name,
|
||||
task.exit_code, task.runtime_ms))
|
||||
|
||||
if self.output_dir is None:
|
||||
# Try to remove the file 100 times (sleeping for 0.1 second in between).
|
||||
# This is a workaround for a process handle seemingly holding on to the
|
||||
# file for too long inside os.subprocess. This workaround is in place
|
||||
# until we figure out a minimal repro to report upstream (or a better
|
||||
# suspect) to prevent os.remove exceptions.
|
||||
num_tries = 100
|
||||
for i in range(num_tries):
|
||||
try:
|
||||
os.remove(task.log_file)
|
||||
except OSError as e:
|
||||
if e.errno is not errno.ENOENT:
|
||||
if i is num_tries - 1:
|
||||
self.out.permanent_line('Could not remove temporary log file: ' +
|
||||
str(e))
|
||||
else:
|
||||
time.sleep(0.1)
|
||||
continue
|
||||
break
|
||||
|
||||
def log_tasks(self, total_tasks):
|
||||
self.total_tasks += total_tasks
|
||||
self.out.transient_line("[0/%d] Running tests..." % self.total_tasks)
|
||||
|
||||
def summarize(self, passed_tasks, failed_tasks, interrupted_tasks):
|
||||
stats = {}
|
||||
|
||||
def add_stats(stats, task, idx):
|
||||
task_key = (task.test_binary, task.test_name)
|
||||
if not task_key in stats:
|
||||
# (passed, failed, interrupted) task_key is added as tie breaker to get
|
||||
# alphabetic sorting on equally-stable tests
|
||||
stats[task_key] = [0, 0, 0, task_key]
|
||||
stats[task_key][idx] += 1
|
||||
|
||||
for task in passed_tasks:
|
||||
add_stats(stats, task, 0)
|
||||
for task in failed_tasks:
|
||||
add_stats(stats, task, 1)
|
||||
for task in interrupted_tasks:
|
||||
add_stats(stats, task, 2)
|
||||
|
||||
self.out.permanent_line("SUMMARY:")
|
||||
for task_key in sorted(stats, key=stats.__getitem__):
|
||||
(num_passed, num_failed, num_interrupted, _) = stats[task_key]
|
||||
(test_binary, task_name) = task_key
|
||||
total_runs = num_passed + num_failed + num_interrupted
|
||||
if num_passed == total_runs:
|
||||
continue
|
||||
self.out.permanent_line(" %s %s passed %d / %d times%s." %
|
||||
(test_binary, task_name, num_passed, total_runs,
|
||||
"" if num_interrupted == 0 else
|
||||
(" (%d interrupted)" % num_interrupted)))
|
||||
|
||||
def flush(self):
|
||||
self.out.flush_transient_output()
|
||||
|
||||
|
||||
class CollectTestResults(object):
|
||||
def __init__(self, json_dump_filepath):
|
||||
self.test_results_lock = threading.Lock()
|
||||
self.json_dump_file = open(json_dump_filepath, 'w')
|
||||
self.test_results = {
|
||||
"interrupted": False,
|
||||
"path_delimiter": ".",
|
||||
# Third version of the file format. See the link in the flag description
|
||||
# for details.
|
||||
"version": 3,
|
||||
"seconds_since_epoch": int(time.time()),
|
||||
"num_failures_by_type": {
|
||||
"PASS": 0,
|
||||
"FAIL": 0,
|
||||
"TIMEOUT": 0,
|
||||
},
|
||||
"tests": {},
|
||||
}
|
||||
|
||||
def log(self, test, runtime_seconds, exit_code):
|
||||
if exit_code is None:
|
||||
actual_result = "TIMEOUT"
|
||||
elif exit_code == 0:
|
||||
actual_result = "PASS"
|
||||
else:
|
||||
actual_result = "FAIL"
|
||||
with self.test_results_lock:
|
||||
self.test_results['num_failures_by_type'][actual_result] += 1
|
||||
results = self.test_results['tests']
|
||||
for name in test.split('.'):
|
||||
results = results.setdefault(name, {})
|
||||
|
||||
if results:
|
||||
results['actual'] += ' ' + actual_result
|
||||
results['times'].append(runtime_seconds)
|
||||
else: # This is the first invocation of the test
|
||||
results['actual'] = actual_result
|
||||
results['times'] = [runtime_seconds]
|
||||
results['time'] = runtime_seconds
|
||||
results['expected'] = 'PASS'
|
||||
|
||||
def dump_to_file_and_close(self):
|
||||
json.dump(self.test_results, self.json_dump_file)
|
||||
self.json_dump_file.close()
|
||||
|
||||
|
||||
# Record of test runtimes. Has built-in locking.
|
||||
class TestTimes(object):
|
||||
class LockedFile(object):
|
||||
def __init__(self, filename, mode):
|
||||
self._filename = filename
|
||||
self._mode = mode
|
||||
self._fo = None
|
||||
|
||||
def __enter__(self):
|
||||
self._fo = open(self._filename, self._mode)
|
||||
|
||||
# Regardless of opening mode we always seek to the beginning of file.
|
||||
# This simplifies code working with LockedFile and also ensures that
|
||||
# we lock (and unlock below) always the same region in file on win32.
|
||||
self._fo.seek(0)
|
||||
|
||||
try:
|
||||
if sys.platform == 'win32':
|
||||
# We are locking here fixed location in file to use it as
|
||||
# an exclusive lock on entire file.
|
||||
msvcrt.locking(self._fo.fileno(), msvcrt.LK_LOCK, 1)
|
||||
else:
|
||||
fcntl.flock(self._fo.fileno(), fcntl.LOCK_EX)
|
||||
except IOError:
|
||||
self._fo.close()
|
||||
raise
|
||||
|
||||
return self._fo
|
||||
|
||||
def __exit__(self, exc_type, exc_value, traceback):
|
||||
# Flush any buffered data to disk. This is needed to prevent race
|
||||
# condition which happens from the moment of releasing file lock
|
||||
# till closing the file.
|
||||
self._fo.flush()
|
||||
|
||||
try:
|
||||
if sys.platform == 'win32':
|
||||
self._fo.seek(0)
|
||||
msvcrt.locking(self._fo.fileno(), msvcrt.LK_UNLCK, 1)
|
||||
else:
|
||||
fcntl.flock(self._fo.fileno(), fcntl.LOCK_UN)
|
||||
finally:
|
||||
self._fo.close()
|
||||
|
||||
return exc_value is None
|
||||
|
||||
def __init__(self, save_file):
|
||||
"Create new object seeded with saved test times from the given file."
|
||||
self.__times = {} # (test binary, test name) -> runtime in ms
|
||||
|
||||
# Protects calls to record_test_time(); other calls are not
|
||||
# expected to be made concurrently.
|
||||
self.__lock = threading.Lock()
|
||||
|
||||
try:
|
||||
with TestTimes.LockedFile(save_file, 'rb') as fd:
|
||||
times = TestTimes.__read_test_times_file(fd)
|
||||
except IOError:
|
||||
# We couldn't obtain the lock.
|
||||
return
|
||||
|
||||
# Discard saved times if the format isn't right.
|
||||
if type(times) is not dict:
|
||||
return
|
||||
for ((test_binary, test_name), runtime) in times.items():
|
||||
if (type(test_binary) is not str or type(test_name) is not str
|
||||
or type(runtime) not in {int, long, type(None)}):
|
||||
return
|
||||
|
||||
self.__times = times
|
||||
|
||||
def get_test_time(self, binary, testname):
|
||||
"""Return the last duration for the given test as an integer number of
|
||||
milliseconds, or None if the test failed or if there's no record for it."""
|
||||
return self.__times.get((binary, testname), None)
|
||||
|
||||
def record_test_time(self, binary, testname, runtime_ms):
|
||||
"""Record that the given test ran in the specified number of
|
||||
milliseconds. If the test failed, runtime_ms should be None."""
|
||||
with self.__lock:
|
||||
self.__times[(binary, testname)] = runtime_ms
|
||||
|
||||
def write_to_file(self, save_file):
|
||||
"Write all the times to file."
|
||||
try:
|
||||
with TestTimes.LockedFile(save_file, 'a+b') as fd:
|
||||
times = TestTimes.__read_test_times_file(fd)
|
||||
|
||||
if times is None:
|
||||
times = self.__times
|
||||
else:
|
||||
times.update(self.__times)
|
||||
|
||||
# We erase data from file while still holding a lock to it. This
|
||||
# way reading old test times and appending new ones are atomic
|
||||
# for external viewer.
|
||||
fd.seek(0)
|
||||
fd.truncate()
|
||||
with gzip.GzipFile(fileobj=fd, mode='wb') as gzf:
|
||||
cPickle.dump(times, gzf, PICKLE_HIGHEST_PROTOCOL)
|
||||
except IOError:
|
||||
pass # ignore errors---saving the times isn't that important
|
||||
|
||||
@staticmethod
|
||||
def __read_test_times_file(fd):
|
||||
try:
|
||||
with gzip.GzipFile(fileobj=fd, mode='rb') as gzf:
|
||||
times = cPickle.load(gzf)
|
||||
except Exception:
|
||||
# File doesn't exist, isn't readable, is malformed---whatever.
|
||||
# Just ignore it.
|
||||
return None
|
||||
else:
|
||||
return times
|
||||
|
||||
|
||||
def find_tests(binaries, additional_args, options, times):
|
||||
test_count = 0
|
||||
tasks = []
|
||||
for test_binary in binaries:
|
||||
command = [test_binary] + additional_args
|
||||
if options.gtest_also_run_disabled_tests:
|
||||
command += ['--gtest_also_run_disabled_tests']
|
||||
|
||||
list_command = command + ['--gtest_list_tests']
|
||||
if options.gtest_filter != '':
|
||||
list_command += ['--gtest_filter=' + options.gtest_filter]
|
||||
|
||||
try:
|
||||
test_list = subprocess.check_output(list_command,
|
||||
stderr=subprocess.STDOUT)
|
||||
except subprocess.CalledProcessError as e:
|
||||
sys.exit("%s: %s\n%s" % (test_binary, str(e), e.output))
|
||||
|
||||
try:
|
||||
test_list = test_list.split('\n')
|
||||
except TypeError:
|
||||
# subprocess.check_output() returns bytes in python3
|
||||
test_list = test_list.decode(sys.stdout.encoding).split('\n')
|
||||
|
||||
command += ['--gtest_color=' + options.gtest_color]
|
||||
|
||||
test_group = ''
|
||||
for line in test_list:
|
||||
if not line.strip():
|
||||
continue
|
||||
if line[0] != " ":
|
||||
# Remove comments for typed tests and strip whitespace.
|
||||
test_group = line.split('#')[0].strip()
|
||||
continue
|
||||
# Remove comments for parameterized tests and strip whitespace.
|
||||
line = line.split('#')[0].strip()
|
||||
if not line:
|
||||
continue
|
||||
|
||||
test_name = test_group + line
|
||||
if not options.gtest_also_run_disabled_tests and 'DISABLED_' in test_name:
|
||||
continue
|
||||
|
||||
# Skip PRE_ tests which are used by Chromium.
|
||||
if '.PRE_' in test_name:
|
||||
continue
|
||||
|
||||
last_execution_time = times.get_test_time(test_binary, test_name)
|
||||
if options.failed and last_execution_time is not None:
|
||||
continue
|
||||
|
||||
test_command = command + ['--gtest_filter=' + test_name]
|
||||
if (test_count - options.shard_index) % options.shard_count == 0:
|
||||
for execution_number in range(options.repeat):
|
||||
tasks.append(
|
||||
Task(test_binary, test_name, test_command, execution_number + 1,
|
||||
last_execution_time, options.output_dir))
|
||||
|
||||
test_count += 1
|
||||
|
||||
# Sort the tasks to run the slowest tests first, so that faster ones can be
|
||||
# finished in parallel.
|
||||
return sorted(tasks, reverse=True)
|
||||
|
||||
|
||||
def execute_tasks(tasks, pool_size, task_manager, timeout_seconds,
|
||||
timeout_per_test, serialize_test_cases):
|
||||
class WorkerFn(object):
|
||||
def __init__(self, tasks, running_groups, timeout_per_test):
|
||||
self.tasks = tasks
|
||||
self.running_groups = running_groups
|
||||
self.timeout_per_test = timeout_per_test
|
||||
self.task_lock = threading.Lock()
|
||||
|
||||
def __call__(self):
|
||||
while True:
|
||||
with self.task_lock:
|
||||
for task_id in range(len(self.tasks)):
|
||||
task = self.tasks[task_id]
|
||||
|
||||
if self.running_groups is not None:
|
||||
test_group = task.test_name.split('.')[0]
|
||||
if test_group in self.running_groups:
|
||||
# Try to find other non-running test group.
|
||||
continue
|
||||
else:
|
||||
self.running_groups.add(test_group)
|
||||
|
||||
del self.tasks[task_id]
|
||||
break
|
||||
else:
|
||||
# Either there is no tasks left or number or remaining test
|
||||
# cases (groups) is less than number or running threads.
|
||||
return
|
||||
|
||||
task_manager.run_task(task, self.timeout_per_test)
|
||||
|
||||
if self.running_groups is not None:
|
||||
with self.task_lock:
|
||||
self.running_groups.remove(test_group)
|
||||
|
||||
def start_daemon(func):
|
||||
t = threading.Thread(target=func)
|
||||
t.daemon = True
|
||||
t.start()
|
||||
return t
|
||||
|
||||
timeout = None
|
||||
try:
|
||||
if timeout_seconds:
|
||||
timeout = threading.Timer(timeout_seconds, sigint_handler.interrupt)
|
||||
timeout.start()
|
||||
running_groups = set() if serialize_test_cases else None
|
||||
worker_fn = WorkerFn(tasks, running_groups, timeout_per_test)
|
||||
workers = [start_daemon(worker_fn) for _ in range(pool_size)]
|
||||
for worker in workers:
|
||||
worker.join()
|
||||
finally:
|
||||
if timeout:
|
||||
timeout.cancel()
|
||||
for task in list(task_manager.started.values()):
|
||||
task.runtime_ms = timeout_seconds * 1000
|
||||
task_manager.register_exit(task)
|
||||
|
||||
|
||||
def default_options_parser():
|
||||
parser = optparse.OptionParser(
|
||||
usage='usage: %prog [options] binary [binary ...] -- [additional args]')
|
||||
|
||||
parser.add_option('-d',
|
||||
'--output_dir',
|
||||
type='string',
|
||||
default=None,
|
||||
help='Output directory for test logs. Logs will be '
|
||||
'available under gtest-parallel-logs/, so '
|
||||
'--output_dir=/tmp will results in all logs being '
|
||||
'available under /tmp/gtest-parallel-logs/.')
|
||||
parser.add_option('-r',
|
||||
'--repeat',
|
||||
type='int',
|
||||
default=1,
|
||||
help='Number of times to execute all the tests.')
|
||||
parser.add_option('--retry_failed',
|
||||
type='int',
|
||||
default=0,
|
||||
help='Number of times to repeat failed tests.')
|
||||
parser.add_option('--failed',
|
||||
action='store_true',
|
||||
default=False,
|
||||
help='run only failed and new tests')
|
||||
parser.add_option('-w',
|
||||
'--workers',
|
||||
type='int',
|
||||
default=multiprocessing.cpu_count(),
|
||||
help='number of workers to spawn')
|
||||
parser.add_option('--gtest_color',
|
||||
type='string',
|
||||
default='yes',
|
||||
help='color output')
|
||||
parser.add_option('--gtest_filter',
|
||||
type='string',
|
||||
default='',
|
||||
help='test filter')
|
||||
parser.add_option('--gtest_also_run_disabled_tests',
|
||||
action='store_true',
|
||||
default=False,
|
||||
help='run disabled tests too')
|
||||
parser.add_option(
|
||||
'--print_test_times',
|
||||
action='store_true',
|
||||
default=False,
|
||||
help='list the run time of each test at the end of execution')
|
||||
parser.add_option(
|
||||
'--print_test_command',
|
||||
action='store_true',
|
||||
default=False,
|
||||
help='Print full test command instead of name')
|
||||
parser.add_option('--shard_count',
|
||||
type='int',
|
||||
default=1,
|
||||
help='total number of shards (for sharding test execution '
|
||||
'between multiple machines)')
|
||||
parser.add_option('--shard_index',
|
||||
type='int',
|
||||
default=0,
|
||||
help='zero-indexed number identifying this shard (for '
|
||||
'sharding test execution between multiple machines)')
|
||||
parser.add_option(
|
||||
'--dump_json_test_results',
|
||||
type='string',
|
||||
default=None,
|
||||
help='Saves the results of the tests as a JSON machine-'
|
||||
'readable file. The format of the file is specified at '
|
||||
'https://www.chromium.org/developers/the-json-test-results-format')
|
||||
parser.add_option('--timeout',
|
||||
type='int',
|
||||
default=None,
|
||||
help='Interrupt all remaining processes after the given '
|
||||
'time (in seconds).')
|
||||
parser.add_option('--timeout_per_test',
|
||||
type='int',
|
||||
default=None,
|
||||
help='Interrupt single processes after the given '
|
||||
'time (in seconds).')
|
||||
parser.add_option('--serialize_test_cases',
|
||||
action='store_true',
|
||||
default=False,
|
||||
help='Do not run tests from the same test '
|
||||
'case in parallel.')
|
||||
return parser
|
||||
|
||||
|
||||
def main():
|
||||
# Remove additional arguments (anything after --).
|
||||
additional_args = []
|
||||
|
||||
for i in range(len(sys.argv)):
|
||||
if sys.argv[i] == '--':
|
||||
additional_args = sys.argv[i + 1:]
|
||||
sys.argv = sys.argv[:i]
|
||||
break
|
||||
|
||||
parser = default_options_parser()
|
||||
(options, binaries) = parser.parse_args()
|
||||
|
||||
if (options.output_dir is not None and not os.path.isdir(options.output_dir)):
|
||||
parser.error('--output_dir value must be an existing directory, '
|
||||
'current value is "%s"' % options.output_dir)
|
||||
|
||||
# Append gtest-parallel-logs to log output, this is to avoid deleting user
|
||||
# data if an user passes a directory where files are already present. If a
|
||||
# user specifies --output_dir=Docs/, we'll create Docs/gtest-parallel-logs
|
||||
# and clean that directory out on startup, instead of nuking Docs/.
|
||||
if options.output_dir:
|
||||
options.output_dir = os.path.join(options.output_dir, 'gtest-parallel-logs')
|
||||
|
||||
if binaries == []:
|
||||
parser.print_usage()
|
||||
sys.exit(1)
|
||||
|
||||
if options.shard_count < 1:
|
||||
parser.error("Invalid number of shards: %d. Must be at least 1." %
|
||||
options.shard_count)
|
||||
if not (0 <= options.shard_index < options.shard_count):
|
||||
parser.error("Invalid shard index: %d. Must be between 0 and %d "
|
||||
"(less than the number of shards)." %
|
||||
(options.shard_index, options.shard_count - 1))
|
||||
|
||||
# Check that all test binaries have an unique basename. That way we can ensure
|
||||
# the logs are saved to unique files even when two different binaries have
|
||||
# common tests.
|
||||
unique_binaries = set(os.path.basename(binary) for binary in binaries)
|
||||
assert len(unique_binaries) == len(binaries), (
|
||||
"All test binaries must have an unique basename.")
|
||||
|
||||
if options.output_dir:
|
||||
# Remove files from old test runs.
|
||||
if os.path.isdir(options.output_dir):
|
||||
shutil.rmtree(options.output_dir)
|
||||
# Create directory for test log output.
|
||||
try:
|
||||
os.makedirs(options.output_dir)
|
||||
except OSError as e:
|
||||
# Ignore errors if this directory already exists.
|
||||
if e.errno != errno.EEXIST or not os.path.isdir(options.output_dir):
|
||||
raise e
|
||||
|
||||
test_results = None
|
||||
if options.dump_json_test_results is not None:
|
||||
test_results = CollectTestResults(options.dump_json_test_results)
|
||||
|
||||
save_file = get_save_file_path()
|
||||
|
||||
times = TestTimes(save_file)
|
||||
logger = FilterFormat(options.output_dir)
|
||||
|
||||
task_manager = TaskManager(times, logger, test_results, Task,
|
||||
options.retry_failed, options.repeat + 1)
|
||||
|
||||
tasks = find_tests(binaries, additional_args, options, times)
|
||||
logger.log_tasks(len(tasks))
|
||||
execute_tasks(tasks, options.workers, task_manager, options.timeout,
|
||||
options.timeout_per_test, options.serialize_test_cases)
|
||||
|
||||
print_try_number = options.retry_failed > 0 or options.repeat > 1
|
||||
if task_manager.passed:
|
||||
logger.move_to('passed', task_manager.passed)
|
||||
if options.print_test_times:
|
||||
logger.print_tests('PASSED TESTS', task_manager.passed, print_try_number, options.print_test_command)
|
||||
|
||||
if task_manager.failed:
|
||||
logger.print_tests('FAILED TESTS', task_manager.failed, print_try_number, options.print_test_command)
|
||||
logger.move_to('failed', task_manager.failed)
|
||||
|
||||
if task_manager.timed_out:
|
||||
logger.print_tests('TIMED OUT TESTS', task_manager.timed_out, print_try_number, options.print_test_command)
|
||||
logger.move_to('timed_out', task_manager.timed_out)
|
||||
|
||||
if task_manager.started:
|
||||
logger.print_tests('INTERRUPTED TESTS', task_manager.started.values(),
|
||||
print_try_number, options.print_test_command)
|
||||
logger.move_to('interrupted', task_manager.started.values())
|
||||
|
||||
if options.repeat > 1 and (task_manager.failed or task_manager.started):
|
||||
logger.summarize(task_manager.passed, task_manager.failed,
|
||||
task_manager.started.values())
|
||||
|
||||
logger.flush()
|
||||
times.write_to_file(save_file)
|
||||
if test_results:
|
||||
test_results.dump_to_file_and_close()
|
||||
|
||||
if sigint_handler.got_sigint():
|
||||
return -signal.SIGINT
|
||||
|
||||
return task_manager.global_exit_code
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
@@ -103,26 +103,31 @@ function main() {
|
||||
gem_install fpm
|
||||
|
||||
make static_lib
|
||||
LIBDIR=/usr/lib
|
||||
if [[ $FPM_OUTPUT = "rpm" ]]; then
|
||||
LIBDIR=$(rpm --eval '%_libdir')
|
||||
fi
|
||||
make install INSTALL_PATH=package
|
||||
|
||||
rm -rf package
|
||||
make install DESTDIR=package PREFIX=/usr LIBDIR=$LIBDIR
|
||||
cd package
|
||||
|
||||
LIB_DIR=lib
|
||||
if [[ -z "$ARCH" ]]; then
|
||||
ARCH=$(getconf LONG_BIT)
|
||||
fi
|
||||
if [[ ("$FPM_OUTPUT" = "rpm") && ($ARCH -eq 64) ]]; then
|
||||
mv lib lib64
|
||||
LIB_DIR=lib64
|
||||
fi
|
||||
|
||||
fpm \
|
||||
-s dir \
|
||||
-t $FPM_OUTPUT \
|
||||
-C package \
|
||||
-n rocksdb \
|
||||
-v $1 \
|
||||
--prefix /usr \
|
||||
--url http://rocksdb.org/ \
|
||||
-m rocksdb@fb.com \
|
||||
--license BSD \
|
||||
--vendor Facebook \
|
||||
--description "RocksDB is an embeddable persistent key-value store for fast storage." \
|
||||
usr
|
||||
include $LIB_DIR
|
||||
}
|
||||
|
||||
# shellcheck disable=SC2068
|
||||
|
||||
Executable
+209
@@ -0,0 +1,209 @@
|
||||
#!/usr/local/fbcode/gcc-4.9-glibc-2.20-fb/bin/python2.7
|
||||
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
|
||||
|
||||
from __future__ import absolute_import
|
||||
from __future__ import division
|
||||
from __future__ import print_function
|
||||
from __future__ import unicode_literals
|
||||
import argparse
|
||||
import commands
|
||||
import subprocess
|
||||
import sys
|
||||
import re
|
||||
import os
|
||||
import time
|
||||
|
||||
|
||||
#
|
||||
# Simple logger
|
||||
#
|
||||
|
||||
class Log:
|
||||
|
||||
def __init__(self, filename):
|
||||
self.filename = filename
|
||||
self.f = open(self.filename, 'w+', 0)
|
||||
|
||||
def caption(self, str):
|
||||
line = "\n##### %s #####\n" % str
|
||||
if self.f:
|
||||
self.f.write("%s \n" % line)
|
||||
else:
|
||||
print(line)
|
||||
|
||||
def error(self, str):
|
||||
data = "\n\n##### ERROR ##### %s" % str
|
||||
if self.f:
|
||||
self.f.write("%s \n" % data)
|
||||
else:
|
||||
print(data)
|
||||
|
||||
def log(self, str):
|
||||
if self.f:
|
||||
self.f.write("%s \n" % str)
|
||||
else:
|
||||
print(str)
|
||||
|
||||
#
|
||||
# Shell Environment
|
||||
#
|
||||
|
||||
|
||||
class Env(object):
|
||||
|
||||
def __init__(self, logfile, tests):
|
||||
self.tests = tests
|
||||
self.log = Log(logfile)
|
||||
|
||||
def shell(self, cmd, path=os.getcwd()):
|
||||
if path:
|
||||
os.chdir(path)
|
||||
|
||||
self.log.log("==== shell session ===========================")
|
||||
self.log.log("%s> %s" % (path, cmd))
|
||||
status = subprocess.call("cd %s; %s" % (path, cmd), shell=True,
|
||||
stdout=self.log.f, stderr=self.log.f)
|
||||
self.log.log("status = %s" % status)
|
||||
self.log.log("============================================== \n\n")
|
||||
return status
|
||||
|
||||
def GetOutput(self, cmd, path=os.getcwd()):
|
||||
if path:
|
||||
os.chdir(path)
|
||||
|
||||
self.log.log("==== shell session ===========================")
|
||||
self.log.log("%s> %s" % (path, cmd))
|
||||
status, out = commands.getstatusoutput(cmd)
|
||||
self.log.log("status = %s" % status)
|
||||
self.log.log("out = %s" % out)
|
||||
self.log.log("============================================== \n\n")
|
||||
return status, out
|
||||
|
||||
#
|
||||
# Pre-commit checker
|
||||
#
|
||||
|
||||
|
||||
class PreCommitChecker(Env):
|
||||
|
||||
def __init__(self, args):
|
||||
Env.__init__(self, args.logfile, args.tests)
|
||||
self.ignore_failure = args.ignore_failure
|
||||
|
||||
#
|
||||
# Get commands for a given job from the determinator file
|
||||
#
|
||||
def get_commands(self, test):
|
||||
status, out = self.GetOutput(
|
||||
"RATIO=1 build_tools/rocksdb-lego-determinator %s" % test, ".")
|
||||
return status, out
|
||||
|
||||
#
|
||||
# Run a specific CI job
|
||||
#
|
||||
def run_test(self, test):
|
||||
self.log.caption("Running test %s locally" % test)
|
||||
|
||||
# get commands for the CI job determinator
|
||||
status, cmds = self.get_commands(test)
|
||||
if status != 0:
|
||||
self.log.error("Error getting commands for test %s" % test)
|
||||
return False
|
||||
|
||||
# Parse the JSON to extract the commands to run
|
||||
cmds = re.findall("'shell':'([^\']*)'", cmds)
|
||||
|
||||
if len(cmds) == 0:
|
||||
self.log.log("No commands found")
|
||||
return False
|
||||
|
||||
# Run commands
|
||||
for cmd in cmds:
|
||||
# Replace J=<..> with the local environment variable
|
||||
if "J" in os.environ:
|
||||
cmd = cmd.replace("J=1", "J=%s" % os.environ["J"])
|
||||
cmd = cmd.replace("make ", "make -j%s " % os.environ["J"])
|
||||
# Run the command
|
||||
status = self.shell(cmd, ".")
|
||||
if status != 0:
|
||||
self.log.error("Error running command %s for test %s"
|
||||
% (cmd, test))
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
#
|
||||
# Run specified CI jobs
|
||||
#
|
||||
def run_tests(self):
|
||||
if not self.tests:
|
||||
self.log.error("Invalid args. Please provide tests")
|
||||
return False
|
||||
|
||||
self.print_separator()
|
||||
self.print_row("TEST", "RESULT")
|
||||
self.print_separator()
|
||||
|
||||
result = True
|
||||
for test in self.tests:
|
||||
start_time = time.time()
|
||||
self.print_test(test)
|
||||
result = self.run_test(test)
|
||||
elapsed_min = (time.time() - start_time) / 60
|
||||
if not result:
|
||||
self.log.error("Error running test %s" % test)
|
||||
self.print_result("FAIL (%dm)" % elapsed_min)
|
||||
if not self.ignore_failure:
|
||||
return False
|
||||
result = False
|
||||
else:
|
||||
self.print_result("PASS (%dm)" % elapsed_min)
|
||||
|
||||
self.print_separator()
|
||||
return result
|
||||
|
||||
#
|
||||
# Print a line
|
||||
#
|
||||
def print_separator(self):
|
||||
print("".ljust(60, "-"))
|
||||
|
||||
#
|
||||
# Print two colums
|
||||
#
|
||||
def print_row(self, c0, c1):
|
||||
print("%s%s" % (c0.ljust(40), c1.ljust(20)))
|
||||
|
||||
def print_test(self, test):
|
||||
print(test.ljust(40), end="")
|
||||
sys.stdout.flush()
|
||||
|
||||
def print_result(self, result):
|
||||
print(result.ljust(20))
|
||||
|
||||
#
|
||||
# Main
|
||||
#
|
||||
parser = argparse.ArgumentParser(description='RocksDB pre-commit checker.')
|
||||
|
||||
# --log <logfile>
|
||||
parser.add_argument('--logfile', default='/tmp/precommit-check.log',
|
||||
help='Log file. Default is /tmp/precommit-check.log')
|
||||
# --ignore_failure
|
||||
parser.add_argument('--ignore_failure', action='store_true', default=False,
|
||||
help='Stop when an error occurs')
|
||||
# <test ....>
|
||||
parser.add_argument('tests', nargs='+',
|
||||
help='CI test(s) to run. e.g: unit punit asan tsan ubsan')
|
||||
|
||||
args = parser.parse_args()
|
||||
checker = PreCommitChecker(args)
|
||||
|
||||
print("Please follow log %s" % checker.log.filename)
|
||||
|
||||
if not checker.run_tests():
|
||||
print("Error running tests. Please check log file %s"
|
||||
% checker.log.filename)
|
||||
sys.exit(1)
|
||||
|
||||
sys.exit(0)
|
||||
@@ -1,38 +0,0 @@
|
||||
#!/usr/bin/env perl
|
||||
|
||||
use strict;
|
||||
|
||||
open(my $ps, "-|", "ps -wwf");
|
||||
my $cols_known = 0;
|
||||
my $cmd_col = 0;
|
||||
my $pid_col = 0;
|
||||
while (<$ps>) {
|
||||
print;
|
||||
my @cols = split(/\s+/);
|
||||
|
||||
if (!$cols_known && /CMD/) {
|
||||
# Parse relevant ps column headers
|
||||
for (my $i = 0; $i <= $#cols; $i++) {
|
||||
if ($cols[$i] eq "CMD") {
|
||||
$cmd_col = $i;
|
||||
}
|
||||
if ($cols[$i] eq "PID") {
|
||||
$pid_col = $i;
|
||||
}
|
||||
}
|
||||
$cols_known = 1;
|
||||
} else {
|
||||
my $pid = $cols[$pid_col];
|
||||
my $cmd = $cols[$cmd_col];
|
||||
# Match numeric PID and relative path command
|
||||
# -> The intention is only to dump stack traces for hangs in code under
|
||||
# test, which means we probably just built it and are executing by
|
||||
# relative path (e.g. ./my_test or foo/bar_test) rather then by absolute
|
||||
# path (e.g. /usr/bin/time) or PATH search (e.g. grep).
|
||||
if ($pid =~ /^[0-9]+$/ && $cmd =~ /^[^\/ ]+[\/]/) {
|
||||
print "Dumping stacks for $pid...\n";
|
||||
system("pstack $pid || gdb -batch -p $pid -ex 'thread apply all bt'");
|
||||
}
|
||||
}
|
||||
}
|
||||
close $ps;
|
||||
@@ -20,11 +20,26 @@ STAT_FILE=${STAT_FILE:-$(mktemp -t -u rocksdb_test_stats_XXXX)}
|
||||
|
||||
function cleanup {
|
||||
rm -rf $DATA_DIR
|
||||
rm -f $STAT_FILE.*
|
||||
rm -f $STAT_FILE.fillseq
|
||||
rm -f $STAT_FILE.readrandom
|
||||
rm -f $STAT_FILE.overwrite
|
||||
rm -f $STAT_FILE.memtablefillreadrandom
|
||||
}
|
||||
|
||||
trap cleanup EXIT
|
||||
|
||||
if [ -z $GIT_BRANCH ]; then
|
||||
git_br=`git rev-parse --abbrev-ref HEAD`
|
||||
else
|
||||
git_br=$(basename $GIT_BRANCH)
|
||||
fi
|
||||
|
||||
if [ $git_br == "master" ]; then
|
||||
git_br=""
|
||||
else
|
||||
git_br="."$git_br
|
||||
fi
|
||||
|
||||
make release
|
||||
|
||||
# measure fillseq + fill up the DB for overwrite benchmark
|
||||
@@ -258,6 +273,7 @@ common_in_mem_args="--db=/dev/shm/rocksdb \
|
||||
--value_size=100 \
|
||||
--compression_type=none \
|
||||
--compression_ratio=1 \
|
||||
--hard_rate_limit=2 \
|
||||
--write_buffer_size=134217728 \
|
||||
--max_write_buffer_number=4 \
|
||||
--level0_file_num_compaction_trigger=8 \
|
||||
@@ -270,10 +286,12 @@ common_in_mem_args="--db=/dev/shm/rocksdb \
|
||||
--sync=0 \
|
||||
--verify_checksum=1 \
|
||||
--delete_obsolete_files_period_micros=314572800 \
|
||||
--max_grandparent_overlap_factor=10 \
|
||||
--use_plain_table=1 \
|
||||
--open_files=-1 \
|
||||
--mmap_read=1 \
|
||||
--mmap_write=0 \
|
||||
--memtablerep=prefix_hash \
|
||||
--bloom_bits=10 \
|
||||
--bloom_locality=1 \
|
||||
--perf_level=0"
|
||||
@@ -360,7 +378,7 @@ function send_to_ods {
|
||||
echo >&2 "ERROR: Key $key doesn't have a value."
|
||||
return
|
||||
fi
|
||||
curl --silent "https://www.facebook.com/intern/agent/ods_set.php?entity=rocksdb_build&key=$key&value=$value" \
|
||||
curl -s "https://www.intern.facebook.com/intern/agent/ods_set.php?entity=rocksdb_build$git_br&key=$key&value=$value" \
|
||||
--connect-timeout 60
|
||||
}
|
||||
|
||||
|
||||
Executable
+949
@@ -0,0 +1,949 @@
|
||||
#!/usr/bin/env bash
|
||||
# This script is executed by Sandcastle
|
||||
# to determine next steps to run
|
||||
|
||||
# Usage:
|
||||
# EMAIL=<email> ONCALL=<email> TRIGGER=<trigger> SUBSCRIBER=<email> rocks_ci.py <test-name>
|
||||
#
|
||||
# Input Value
|
||||
# -------------------------------------------------------------------------
|
||||
# EMAIL Email address to report on trigger conditions
|
||||
# ONCALL Email address to raise a task on failure
|
||||
# TRIGGER Trigger conditions for email. Valid values are fail, warn, all
|
||||
# SUBSCRIBER Email addresss to add as subscriber for task
|
||||
#
|
||||
|
||||
#
|
||||
# Report configuration
|
||||
#
|
||||
REPORT_EMAIL=
|
||||
if [ ! -z $EMAIL ]; then
|
||||
if [ -z $TRIGGER ]; then
|
||||
TRIGGER="fail"
|
||||
fi
|
||||
|
||||
REPORT_EMAIL="
|
||||
{
|
||||
'type':'email',
|
||||
'triggers': [ '$TRIGGER' ],
|
||||
'emails':['$EMAIL']
|
||||
},"
|
||||
fi
|
||||
|
||||
CREATE_TASK=
|
||||
if [ ! -z $ONCALL ]; then
|
||||
CREATE_TASK="
|
||||
{
|
||||
'type':'task',
|
||||
'triggers':[ 'fail' ],
|
||||
'priority':0,
|
||||
'subscribers':[ '$SUBSCRIBER' ],
|
||||
'tags':[ 'rocksdb', 'ci' ],
|
||||
},"
|
||||
fi
|
||||
|
||||
# For now, create the tasks using only the dedicated task creation tool.
|
||||
CREATE_TASK=
|
||||
|
||||
REPORT=
|
||||
if [[ ! -z $REPORT_EMAIL || ! -z $CREATE_TASK ]]; then
|
||||
REPORT="'report': [
|
||||
$REPORT_EMAIL
|
||||
$CREATE_TASK
|
||||
]"
|
||||
fi
|
||||
|
||||
#
|
||||
# Helper variables
|
||||
#
|
||||
CLEANUP_ENV="
|
||||
{
|
||||
'name':'Cleanup environment',
|
||||
'shell':'rm -rf /dev/shm/rocksdb && mkdir /dev/shm/rocksdb && (chmod +t /dev/shm || true) && make clean',
|
||||
'user':'root'
|
||||
}"
|
||||
|
||||
UPLOAD_DB_DIR="
|
||||
{
|
||||
'name':'Upload database directory',
|
||||
'shell':'tar -cvzf rocksdb_db.tar.gz /dev/shm/rocksdb/',
|
||||
'user':'root',
|
||||
'cleanup':true,
|
||||
'provide_artifacts': [
|
||||
{
|
||||
'name':'rocksdb_db_dir',
|
||||
'paths': ['rocksdb_db.tar.gz'],
|
||||
'bundle': false,
|
||||
},
|
||||
],
|
||||
}"
|
||||
|
||||
# We will eventually set the RATIO to 1, but we want do this
|
||||
# in steps. RATIO=$(nproc) will make it work as J=1
|
||||
if [ -z $RATIO ]; then
|
||||
RATIO=$(nproc)
|
||||
fi
|
||||
|
||||
if [ -z $PARALLEL_J ]; then
|
||||
PARALLEL_J="J=$(expr $(nproc) / ${RATIO})"
|
||||
fi
|
||||
|
||||
if [ -z $PARALLEL_j ]; then
|
||||
PARALLEL_j="-j$(expr $(nproc) / ${RATIO})"
|
||||
fi
|
||||
|
||||
PARALLELISM="$PARALLEL_J $PARALLEL_j"
|
||||
|
||||
DEBUG="OPT=-g"
|
||||
SHM="TEST_TMPDIR=/dev/shm/rocksdb"
|
||||
NON_SHM="TMPD=/tmp/rocksdb_test_tmp"
|
||||
GCC_481="ROCKSDB_FBCODE_BUILD_WITH_481=1"
|
||||
ASAN="COMPILE_WITH_ASAN=1"
|
||||
CLANG="USE_CLANG=1"
|
||||
# in gcc-5 there are known problems with TSAN like https://gcc.gnu.org/bugzilla/show_bug.cgi?id=71090.
|
||||
# using platform007 gives us gcc-8 or higher which has that bug fixed.
|
||||
TSAN="ROCKSDB_FBCODE_BUILD_WITH_PLATFORM007=1 COMPILE_WITH_TSAN=1"
|
||||
UBSAN="COMPILE_WITH_UBSAN=1"
|
||||
TSAN_CRASH='CRASH_TEST_EXT_ARGS="--compression_type=zstd --log2_keys_per_lock=22"'
|
||||
NON_TSAN_CRASH="CRASH_TEST_EXT_ARGS=--compression_type=zstd"
|
||||
DISABLE_JEMALLOC="DISABLE_JEMALLOC=1"
|
||||
HTTP_PROXY="https_proxy=http://fwdproxy.29.prn1:8080 http_proxy=http://fwdproxy.29.prn1:8080 ftp_proxy=http://fwdproxy.29.prn1:8080"
|
||||
SETUP_JAVA_ENV="export $HTTP_PROXY; export JAVA_HOME=/usr/local/jdk-8u60-64/; export PATH=\$JAVA_HOME/bin:\$PATH"
|
||||
PARSER="'parser':'python build_tools/error_filter.py $1'"
|
||||
|
||||
CONTRUN_NAME="ROCKSDB_CONTRUN_NAME"
|
||||
|
||||
# This code is getting called under various scenarios. What we care about is to
|
||||
# understand when it's called from nightly contruns because in that case we'll
|
||||
# create tasks for any failures. To follow the existing pattern, we'll check
|
||||
# the value of $ONCALL. If it's a diff then just call `false` to make sure
|
||||
# that errors will be properly propagated to the caller.
|
||||
if [ ! -z $ONCALL ]; then
|
||||
TASK_CREATION_TOOL="/usr/local/bin/mysql_mtr_filter --rocksdb --oncall $ONCALL"
|
||||
else
|
||||
TASK_CREATION_TOOL="false"
|
||||
fi
|
||||
|
||||
#
|
||||
# A mechanism to disable tests temporarily
|
||||
#
|
||||
DISABLE_COMMANDS="[
|
||||
{
|
||||
'name':'Disable test',
|
||||
'oncall':'$ONCALL',
|
||||
'steps': [
|
||||
{
|
||||
'name':'Job disabled. Please contact test owner',
|
||||
'shell':'exit 1',
|
||||
'user':'root'
|
||||
},
|
||||
],
|
||||
}
|
||||
]"
|
||||
|
||||
#
|
||||
# RocksDB unit test
|
||||
#
|
||||
UNIT_TEST_COMMANDS="[
|
||||
{
|
||||
'name':'Rocksdb Unit Test',
|
||||
'oncall':'$ONCALL',
|
||||
'executeLocal': 'true',
|
||||
'steps': [
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Build and test RocksDB debug version',
|
||||
'shell':'$SHM $DEBUG make $PARALLELISM check || $CONTRUN_NAME=check $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
],
|
||||
$REPORT
|
||||
}
|
||||
]"
|
||||
|
||||
#
|
||||
# RocksDB unit test not under /dev/shm
|
||||
#
|
||||
UNIT_TEST_NON_SHM_COMMANDS="[
|
||||
{
|
||||
'name':'Rocksdb Unit Test',
|
||||
'oncall':'$ONCALL',
|
||||
'executeLocal': 'true',
|
||||
'timeout': 86400,
|
||||
'steps': [
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Build and test RocksDB debug version',
|
||||
'timeout': 86400,
|
||||
'shell':'$NON_SHM $DEBUG make $PARALLELISM check || $CONTRUN_NAME=non_shm_check $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
],
|
||||
$REPORT
|
||||
}
|
||||
]"
|
||||
|
||||
#
|
||||
# RocksDB release build and unit tests
|
||||
#
|
||||
RELEASE_BUILD_COMMANDS="[
|
||||
{
|
||||
'name':'Rocksdb Release Build',
|
||||
'oncall':'$ONCALL',
|
||||
'executeLocal': 'true',
|
||||
'steps': [
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Build RocksDB release',
|
||||
'shell':'make $PARALLEL_j release || $CONTRUN_NAME=release $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
],
|
||||
$REPORT
|
||||
}
|
||||
]"
|
||||
|
||||
#
|
||||
# RocksDB unit test on gcc-4.8.1
|
||||
#
|
||||
UNIT_TEST_COMMANDS_481="[
|
||||
{
|
||||
'name':'Rocksdb Unit Test on GCC 4.8.1',
|
||||
'oncall':'$ONCALL',
|
||||
'executeLocal': 'true',
|
||||
'steps': [
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Build and test RocksDB debug version',
|
||||
'shell':'$SHM $GCC_481 $DEBUG make $PARALLELISM check || $CONTRUN_NAME=unit_gcc_481_check $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
],
|
||||
$REPORT
|
||||
}
|
||||
]"
|
||||
|
||||
#
|
||||
# RocksDB release build and unit tests
|
||||
#
|
||||
RELEASE_BUILD_COMMANDS_481="[
|
||||
{
|
||||
'name':'Rocksdb Release on GCC 4.8.1',
|
||||
'oncall':'$ONCALL',
|
||||
'executeLocal': 'true',
|
||||
'steps': [
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Build RocksDB release on GCC 4.8.1',
|
||||
'shell':'$GCC_481 make $PARALLEL_j release || $CONTRUN_NAME=release_gcc481 $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
],
|
||||
$REPORT
|
||||
}
|
||||
]"
|
||||
|
||||
#
|
||||
# RocksDB unit test with CLANG
|
||||
#
|
||||
CLANG_UNIT_TEST_COMMANDS="[
|
||||
{
|
||||
'name':'Rocksdb Unit Test',
|
||||
'oncall':'$ONCALL',
|
||||
'executeLocal': 'true',
|
||||
'steps': [
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Build and test RocksDB debug',
|
||||
'shell':'$CLANG $SHM $DEBUG make $PARALLELISM check || $CONTRUN_NAME=clang_check $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
],
|
||||
$REPORT
|
||||
}
|
||||
]"
|
||||
|
||||
#
|
||||
# RocksDB release build with CLANG
|
||||
#
|
||||
CLANG_RELEASE_BUILD_COMMANDS="[
|
||||
{
|
||||
'name':'Rocksdb CLANG Release Build',
|
||||
'oncall':'$ONCALL',
|
||||
'executeLocal': 'true',
|
||||
'steps': [
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Build RocksDB release',
|
||||
'shell':'$CLANG make $PARALLEL_j release|| $CONTRUN_NAME=clang_release $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
],
|
||||
$REPORT
|
||||
}
|
||||
]"
|
||||
|
||||
#
|
||||
# RocksDB analyze
|
||||
#
|
||||
CLANG_ANALYZE_COMMANDS="[
|
||||
{
|
||||
'name':'Rocksdb analyze',
|
||||
'oncall':'$ONCALL',
|
||||
'executeLocal': 'true',
|
||||
'steps': [
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'RocksDB build and analyze',
|
||||
'shell':'$CLANG $SHM $DEBUG make $PARALLEL_j analyze || $CONTRUN_NAME=clang_analyze $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
],
|
||||
$REPORT
|
||||
}
|
||||
]"
|
||||
|
||||
#
|
||||
# RocksDB code coverage
|
||||
#
|
||||
CODE_COV_COMMANDS="[
|
||||
{
|
||||
'name':'Rocksdb Unit Test Code Coverage',
|
||||
'oncall':'$ONCALL',
|
||||
'executeLocal': 'true',
|
||||
'steps': [
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Build, test and collect code coverage info',
|
||||
'shell':'$SHM $DEBUG make $PARALLELISM coverage || $CONTRUN_NAME=coverage $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
],
|
||||
$REPORT
|
||||
}
|
||||
]"
|
||||
|
||||
#
|
||||
# RocksDB unity
|
||||
#
|
||||
UNITY_COMMANDS="[
|
||||
{
|
||||
'name':'Rocksdb Unity',
|
||||
'oncall':'$ONCALL',
|
||||
'executeLocal': 'true',
|
||||
'steps': [
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Build, test unity test',
|
||||
'shell':'$SHM $DEBUG V=1 make J=1 unity_test || $CONTRUN_NAME=unity_test $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
],
|
||||
$REPORT
|
||||
}
|
||||
]"
|
||||
|
||||
#
|
||||
# Build RocksDB lite
|
||||
#
|
||||
LITE_BUILD_COMMANDS="[
|
||||
{
|
||||
'name':'Rocksdb Lite build',
|
||||
'oncall':'$ONCALL',
|
||||
'executeLocal': 'true',
|
||||
'steps': [
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Build RocksDB debug version',
|
||||
'shell':'make J=1 LITE=1 all check || $CONTRUN_NAME=lite $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
],
|
||||
$REPORT
|
||||
}
|
||||
]"
|
||||
|
||||
#
|
||||
# Report RocksDB lite binary size to scuba
|
||||
REPORT_LITE_BINARY_SIZE_COMMANDS="[
|
||||
{
|
||||
'name':'Rocksdb Lite Binary Size',
|
||||
'oncall':'$ONCALL',
|
||||
'executeLocal': 'true',
|
||||
'steps': [
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Report RocksDB Lite binary size to scuba',
|
||||
'shell':'tools/report_lite_binary_size.sh',
|
||||
'user':'root',
|
||||
},
|
||||
],
|
||||
]"
|
||||
|
||||
#
|
||||
# RocksDB stress/crash test
|
||||
#
|
||||
STRESS_CRASH_TEST_COMMANDS="[
|
||||
{
|
||||
'name':'Rocksdb Stress and Crash Test',
|
||||
'oncall':'$ONCALL',
|
||||
'executeLocal': 'true',
|
||||
'timeout': 86400,
|
||||
'steps': [
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Build and run RocksDB debug stress tests',
|
||||
'shell':'$SHM $DEBUG $NON_TSAN_CRASH make J=1 db_stress || $CONTRUN_NAME=db_stress $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
{
|
||||
'name':'Build and run RocksDB debug crash tests',
|
||||
'timeout': 86400,
|
||||
'shell':'$SHM $DEBUG $NON_TSAN_CRASH make J=1 crash_test || $CONTRUN_NAME=crash_test $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
}
|
||||
],
|
||||
$REPORT
|
||||
}
|
||||
]"
|
||||
|
||||
#
|
||||
# RocksDB stress/crash test with atomic flush
|
||||
#
|
||||
STRESS_CRASH_TEST_WITH_ATOMIC_FLUSH_COMMANDS="[
|
||||
{
|
||||
'name':'Rocksdb Stress and Crash Test with atomic flush',
|
||||
'oncall':'$ONCALL',
|
||||
'executeLocal': 'true',
|
||||
'timeout': 86400,
|
||||
'steps': [
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Build and run RocksDB debug stress tests',
|
||||
'shell':'$SHM $DEBUG $NON_TSAN_CRASH make J=1 db_stress || $CONTRUN_NAME=db_stress $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
{
|
||||
'name':'Build and run RocksDB debug crash tests with atomic flush',
|
||||
'timeout': 86400,
|
||||
'shell':'$SHM $DEBUG $NON_TSAN_CRASH make J=1 crash_test_with_atomic_flush || $CONTRUN_NAME=crash_test_with_atomic_flush $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
$UPLOAD_DB_DIR,
|
||||
],
|
||||
$REPORT
|
||||
}
|
||||
]"
|
||||
|
||||
# RocksDB write stress test.
|
||||
# We run on disk device on purpose (i.e. no $SHM)
|
||||
# because we want to add some randomness to fsync commands
|
||||
WRITE_STRESS_COMMANDS="[
|
||||
{
|
||||
'name':'Rocksdb Write Stress Test',
|
||||
'oncall':'$ONCALL',
|
||||
'executeLocal': 'true',
|
||||
'steps': [
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Build and run RocksDB write stress tests',
|
||||
'shell':'make write_stress && python tools/write_stress_runner.py --runtime_sec=3600 --db=/tmp/rocksdb_write_stress || $CONTRUN_NAME=write_stress $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
}
|
||||
],
|
||||
'artifacts': [{'name': 'database', 'paths': ['/tmp/rocksdb_write_stress']}],
|
||||
$REPORT
|
||||
}
|
||||
]"
|
||||
|
||||
|
||||
#
|
||||
# RocksDB test under address sanitizer
|
||||
#
|
||||
ASAN_TEST_COMMANDS="[
|
||||
{
|
||||
'name':'Rocksdb Unit Test under ASAN',
|
||||
'oncall':'$ONCALL',
|
||||
'executeLocal': 'true',
|
||||
'steps': [
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Test RocksDB debug under ASAN',
|
||||
'shell':'set -o pipefail && ($SHM $ASAN $DEBUG make $PARALLELISM asan_check || $CONTRUN_NAME=asan_check $TASK_CREATION_TOOL) |& /usr/facebook/ops/scripts/asan_symbolize.py -d',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
}
|
||||
],
|
||||
$REPORT
|
||||
}
|
||||
]"
|
||||
|
||||
#
|
||||
# RocksDB crash testing under address sanitizer
|
||||
#
|
||||
ASAN_CRASH_TEST_COMMANDS="[
|
||||
{
|
||||
'name':'Rocksdb crash test under ASAN',
|
||||
'oncall':'$ONCALL',
|
||||
'executeLocal': 'true',
|
||||
'timeout': 86400,
|
||||
'steps': [
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Build and run RocksDB debug asan_crash_test',
|
||||
'timeout': 86400,
|
||||
'shell':'$SHM $DEBUG $NON_TSAN_CRASH make J=1 asan_crash_test || $CONTRUN_NAME=asan_crash_test $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
],
|
||||
$REPORT
|
||||
}
|
||||
]"
|
||||
|
||||
#
|
||||
# RocksDB crash testing with atomic flush under address sanitizer
|
||||
#
|
||||
ASAN_CRASH_TEST_WITH_ATOMIC_FLUSH_COMMANDS="[
|
||||
{
|
||||
'name':'Rocksdb crash test with atomic flush under ASAN',
|
||||
'oncall':'$ONCALL',
|
||||
'executeLocal': 'true',
|
||||
'timeout': 86400,
|
||||
'steps': [
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Build and run RocksDB debug asan_crash_test_with_atomic_flush',
|
||||
'timeout': 86400,
|
||||
'shell':'$SHM $DEBUG $NON_TSAN_CRASH make J=1 asan_crash_test_with_atomic_flush || $CONTRUN_NAME=asan_crash_test_with_atomic_flush $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
$UPLOAD_DB_DIR,
|
||||
],
|
||||
$REPORT
|
||||
}
|
||||
]"
|
||||
|
||||
#
|
||||
# RocksDB test under undefined behavior sanitizer
|
||||
#
|
||||
UBSAN_TEST_COMMANDS="[
|
||||
{
|
||||
'name':'Rocksdb Unit Test under UBSAN',
|
||||
'oncall':'$ONCALL',
|
||||
'executeLocal': 'true',
|
||||
'steps': [
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Test RocksDB debug under UBSAN',
|
||||
'shell':'set -o pipefail && $SHM $UBSAN $DEBUG make $PARALLELISM ubsan_check || $CONTRUN_NAME=ubsan_check $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
}
|
||||
],
|
||||
$REPORT
|
||||
}
|
||||
]"
|
||||
|
||||
#
|
||||
# RocksDB crash testing under udnefined behavior sanitizer
|
||||
#
|
||||
UBSAN_CRASH_TEST_COMMANDS="[
|
||||
{
|
||||
'name':'Rocksdb crash test under UBSAN',
|
||||
'oncall':'$ONCALL',
|
||||
'executeLocal': 'true',
|
||||
'timeout': 86400,
|
||||
'steps': [
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Build and run RocksDB debug ubsan_crash_test',
|
||||
'timeout': 86400,
|
||||
'shell':'$SHM $DEBUG $NON_TSAN_CRASH make J=1 ubsan_crash_test || $CONTRUN_NAME=ubsan_crash_test $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
],
|
||||
$REPORT
|
||||
}
|
||||
]"
|
||||
|
||||
#
|
||||
# RocksDB crash testing with atomic flush under undefined behavior sanitizer
|
||||
#
|
||||
UBSAN_CRASH_TEST_WITH_ATOMIC_FLUSH_COMMANDS="[
|
||||
{
|
||||
'name':'Rocksdb crash test with atomic flush under UBSAN',
|
||||
'oncall':'$ONCALL',
|
||||
'executeLocal': 'true',
|
||||
'timeout': 86400,
|
||||
'steps': [
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Build and run RocksDB debug ubsan_crash_test_with_atomic_flush',
|
||||
'timeout': 86400,
|
||||
'shell':'$SHM $DEBUG $NON_TSAN_CRASH make J=1 ubsan_crash_test_with_atomic_flush || $CONTRUN_NAME=ubsan_crash_test_with_atomic_flush $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
$UPLOAD_DB_DIR,
|
||||
],
|
||||
$REPORT
|
||||
}
|
||||
]"
|
||||
|
||||
#
|
||||
# RocksDB unit test under valgrind
|
||||
#
|
||||
VALGRIND_TEST_COMMANDS="[
|
||||
{
|
||||
'name':'Rocksdb Unit Test under valgrind',
|
||||
'oncall':'$ONCALL',
|
||||
'executeLocal': 'true',
|
||||
'timeout': 86400,
|
||||
'steps': [
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Run RocksDB debug unit tests',
|
||||
'timeout': 86400,
|
||||
'shell':'$SHM $DEBUG make $PARALLELISM valgrind_test || $CONTRUN_NAME=valgrind_check $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
],
|
||||
$REPORT
|
||||
}
|
||||
]"
|
||||
|
||||
#
|
||||
# RocksDB test under TSAN
|
||||
#
|
||||
TSAN_UNIT_TEST_COMMANDS="[
|
||||
{
|
||||
'name':'Rocksdb Unit Test under TSAN',
|
||||
'oncall':'$ONCALL',
|
||||
'executeLocal': 'true',
|
||||
'timeout': 86400,
|
||||
'steps': [
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Run RocksDB debug unit test',
|
||||
'timeout': 86400,
|
||||
'shell':'set -o pipefail && $SHM $DEBUG $TSAN make $PARALLELISM check || $CONTRUN_NAME=tsan_check $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
],
|
||||
$REPORT
|
||||
}
|
||||
]"
|
||||
|
||||
#
|
||||
# RocksDB crash test under TSAN
|
||||
#
|
||||
TSAN_CRASH_TEST_COMMANDS="[
|
||||
{
|
||||
'name':'Rocksdb Crash Test under TSAN',
|
||||
'oncall':'$ONCALL',
|
||||
'executeLocal': 'true',
|
||||
'timeout': 86400,
|
||||
'steps': [
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Compile and run',
|
||||
'timeout': 86400,
|
||||
'shell':'set -o pipefail && $SHM $DEBUG $TSAN $TSAN_CRASH CRASH_TEST_KILL_ODD=1887 make J=1 crash_test || $CONTRUN_NAME=tsan_crash_test $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
],
|
||||
$REPORT
|
||||
}
|
||||
]"
|
||||
|
||||
#
|
||||
# RocksDB crash test with atomic flush under TSAN
|
||||
#
|
||||
TSAN_CRASH_TEST_WITH_ATOMIC_FLUSH_COMMANDS="[
|
||||
{
|
||||
'name':'Rocksdb Crash Test with atomic flush under TSAN',
|
||||
'oncall':'$ONCALL',
|
||||
'executeLocal': 'true',
|
||||
'timeout': 86400,
|
||||
'steps': [
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Compile and run',
|
||||
'timeout': 86400,
|
||||
'shell':'set -o pipefail && $SHM $DEBUG $TSAN $TSAN_CRASH CRASH_TEST_KILL_ODD=1887 make J=1 crash_test_with_atomic_flush || $CONTRUN_NAME=tsan_crash_test_with_atomic_flush $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
$UPLOAD_DB_DIR,
|
||||
],
|
||||
$REPORT
|
||||
}
|
||||
]"
|
||||
|
||||
#
|
||||
# RocksDB format compatible
|
||||
#
|
||||
|
||||
run_format_compatible()
|
||||
{
|
||||
export TEST_TMPDIR=/dev/shm/rocksdb
|
||||
rm -rf /dev/shm/rocksdb
|
||||
mkdir /dev/shm/rocksdb
|
||||
|
||||
tools/check_format_compatible.sh
|
||||
}
|
||||
|
||||
FORMAT_COMPATIBLE_COMMANDS="[
|
||||
{
|
||||
'name':'Rocksdb Format Compatible tests',
|
||||
'oncall':'$ONCALL',
|
||||
'executeLocal': 'true',
|
||||
'steps': [
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Run RocksDB debug unit test',
|
||||
'shell':'build_tools/rocksdb-lego-determinator run_format_compatible || $CONTRUN_NAME=run_format_compatible $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
],
|
||||
$REPORT
|
||||
}
|
||||
]"
|
||||
|
||||
#
|
||||
# RocksDB no compression
|
||||
#
|
||||
run_no_compression()
|
||||
{
|
||||
export TEST_TMPDIR=/dev/shm/rocksdb
|
||||
rm -rf /dev/shm/rocksdb
|
||||
mkdir /dev/shm/rocksdb
|
||||
make clean
|
||||
cat build_tools/fbcode_config.sh | grep -iv dzstd | grep -iv dzlib | grep -iv dlz4 | grep -iv dsnappy | grep -iv dbzip2 > .tmp.fbcode_config.sh
|
||||
mv .tmp.fbcode_config.sh build_tools/fbcode_config.sh
|
||||
cat Makefile | grep -v tools/ldb_test.py > .tmp.Makefile
|
||||
mv .tmp.Makefile Makefile
|
||||
make $DEBUG J=1 check
|
||||
}
|
||||
|
||||
NO_COMPRESSION_COMMANDS="[
|
||||
{
|
||||
'name':'Rocksdb No Compression tests',
|
||||
'oncall':'$ONCALL',
|
||||
'executeLocal': 'true',
|
||||
'steps': [
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Run RocksDB debug unit test',
|
||||
'shell':'build_tools/rocksdb-lego-determinator run_no_compression || $CONTRUN_NAME=run_no_compression $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
],
|
||||
$REPORT
|
||||
}
|
||||
]"
|
||||
|
||||
#
|
||||
# RocksDB regression
|
||||
#
|
||||
run_regression()
|
||||
{
|
||||
time -v bash -vx ./build_tools/regression_build_test.sh $(mktemp -d $WORKSPACE/leveldb.XXXX) $(mktemp leveldb_test_stats.XXXX)
|
||||
|
||||
# ======= report size to ODS ========
|
||||
|
||||
# parameters: $1 -- key, $2 -- value
|
||||
function send_size_to_ods {
|
||||
curl -s "https://www.intern.facebook.com/intern/agent/ods_set.php?entity=rocksdb_build&key=rocksdb.build_size.$1&value=$2" \
|
||||
--connect-timeout 60
|
||||
}
|
||||
|
||||
# === normal build ===
|
||||
make clean
|
||||
make -j$(nproc) static_lib
|
||||
send_size_to_ods static_lib $(stat --printf="%s" librocksdb.a)
|
||||
strip librocksdb.a
|
||||
send_size_to_ods static_lib_stripped $(stat --printf="%s" librocksdb.a)
|
||||
|
||||
make -j$(nproc) shared_lib
|
||||
send_size_to_ods shared_lib $(stat --printf="%s" `readlink -f librocksdb.so`)
|
||||
strip `readlink -f librocksdb.so`
|
||||
send_size_to_ods shared_lib_stripped $(stat --printf="%s" `readlink -f librocksdb.so`)
|
||||
|
||||
# === lite build ===
|
||||
make clean
|
||||
make LITE=1 -j$(nproc) static_lib
|
||||
send_size_to_ods static_lib_lite $(stat --printf="%s" librocksdb.a)
|
||||
strip librocksdb.a
|
||||
send_size_to_ods static_lib_lite_stripped $(stat --printf="%s" librocksdb.a)
|
||||
|
||||
make LITE=1 -j$(nproc) shared_lib
|
||||
send_size_to_ods shared_lib_lite $(stat --printf="%s" `readlink -f librocksdb.so`)
|
||||
strip `readlink -f librocksdb.so`
|
||||
send_size_to_ods shared_lib_lite_stripped $(stat --printf="%s" `readlink -f librocksdb.so`)
|
||||
}
|
||||
|
||||
REGRESSION_COMMANDS="[
|
||||
{
|
||||
'name':'Rocksdb regression commands',
|
||||
'oncall':'$ONCALL',
|
||||
'steps': [
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Make and run script',
|
||||
'shell':'build_tools/rocksdb-lego-determinator run_regression || $CONTRUN_NAME=run_regression $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
],
|
||||
$REPORT
|
||||
}
|
||||
]"
|
||||
|
||||
#
|
||||
# RocksDB Java build
|
||||
#
|
||||
JAVA_BUILD_TEST_COMMANDS="[
|
||||
{
|
||||
'name':'Rocksdb Java Build',
|
||||
'oncall':'$ONCALL',
|
||||
'executeLocal': 'true',
|
||||
'steps': [
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Build RocksDB for Java',
|
||||
'shell':'$SETUP_JAVA_ENV; $SHM make rocksdbjava || $CONTRUN_NAME=rocksdbjava $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
],
|
||||
$REPORT
|
||||
}
|
||||
]"
|
||||
|
||||
|
||||
case $1 in
|
||||
unit)
|
||||
echo $UNIT_TEST_COMMANDS
|
||||
;;
|
||||
unit_non_shm)
|
||||
echo $UNIT_TEST_NON_SHM_COMMANDS
|
||||
;;
|
||||
release)
|
||||
echo $RELEASE_BUILD_COMMANDS
|
||||
;;
|
||||
unit_481)
|
||||
echo $UNIT_TEST_COMMANDS_481
|
||||
;;
|
||||
release_481)
|
||||
echo $RELEASE_BUILD_COMMANDS_481
|
||||
;;
|
||||
clang_unit)
|
||||
echo $CLANG_UNIT_TEST_COMMANDS
|
||||
;;
|
||||
clang_release)
|
||||
echo $CLANG_RELEASE_BUILD_COMMANDS
|
||||
;;
|
||||
clang_analyze)
|
||||
echo $CLANG_ANALYZE_COMMANDS
|
||||
;;
|
||||
code_cov)
|
||||
echo $CODE_COV_COMMANDS
|
||||
;;
|
||||
unity)
|
||||
echo $UNITY_COMMANDS
|
||||
;;
|
||||
lite)
|
||||
echo $LITE_BUILD_COMMANDS
|
||||
;;
|
||||
report_lite_binary_size)
|
||||
echo $REPORT_LITE_BINARY_SIZE_COMMANDS
|
||||
;;
|
||||
stress_crash)
|
||||
echo $STRESS_CRASH_TEST_COMMANDS
|
||||
;;
|
||||
stress_crash_with_atomic_flush)
|
||||
echo $STRESS_CRASH_TEST_WITH_ATOMIC_FLUSH_COMMANDS
|
||||
;;
|
||||
write_stress)
|
||||
echo $WRITE_STRESS_COMMANDS
|
||||
;;
|
||||
asan)
|
||||
echo $ASAN_TEST_COMMANDS
|
||||
;;
|
||||
asan_crash)
|
||||
echo $ASAN_CRASH_TEST_COMMANDS
|
||||
;;
|
||||
asan_crash_with_atomic_flush)
|
||||
echo $ASAN_CRASH_TEST_WITH_ATOMIC_FLUSH_COMMANDS
|
||||
;;
|
||||
ubsan)
|
||||
echo $UBSAN_TEST_COMMANDS
|
||||
;;
|
||||
ubsan_crash)
|
||||
echo $UBSAN_CRASH_TEST_COMMANDS
|
||||
;;
|
||||
ubsan_crash_with_atomic_flush)
|
||||
echo $UBSAN_CRASH_TEST_WITH_ATOMIC_FLUSH_COMMANDS
|
||||
;;
|
||||
valgrind)
|
||||
echo $VALGRIND_TEST_COMMANDS
|
||||
;;
|
||||
tsan)
|
||||
echo $TSAN_UNIT_TEST_COMMANDS
|
||||
;;
|
||||
tsan_crash)
|
||||
echo $TSAN_CRASH_TEST_COMMANDS
|
||||
;;
|
||||
tsan_crash_with_atomic_flush)
|
||||
echo $TSAN_CRASH_TEST_WITH_ATOMIC_FLUSH_COMMANDS
|
||||
;;
|
||||
format_compatible)
|
||||
echo $FORMAT_COMPATIBLE_COMMANDS
|
||||
;;
|
||||
run_format_compatible)
|
||||
run_format_compatible
|
||||
;;
|
||||
no_compression)
|
||||
echo $NO_COMPRESSION_COMMANDS
|
||||
;;
|
||||
run_no_compression)
|
||||
run_no_compression
|
||||
;;
|
||||
regression)
|
||||
echo $REGRESSION_COMMANDS
|
||||
;;
|
||||
run_regression)
|
||||
run_regression
|
||||
;;
|
||||
java_build)
|
||||
echo $JAVA_BUILD_TEST_COMMANDS
|
||||
;;
|
||||
*)
|
||||
echo "Invalid determinator command"
|
||||
;;
|
||||
esac
|
||||
@@ -1,67 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
# This source code is licensed under both the GPLv2 (found in the
|
||||
# COPYING file in the root directory) and Apache 2.0 License
|
||||
# (found in the LICENSE.Apache file in the root directory).
|
||||
#
|
||||
# Build RocksDB unit test binaries and run them under gtest-parallel,
|
||||
# which shards the test cases across CPUs for faster execution.
|
||||
#
|
||||
# Hardened version of a simple `make <bin> && gtest-parallel ./<bin>` helper:
|
||||
# * AUTO_CLEAN=1 so the Makefile automatically runs a clean when the build
|
||||
# parameters (DEBUG_LEVEL, sanitizers, ASSERT_STATUS_CHECKED, RTTI, etc.)
|
||||
# have changed since the last build, preventing stale/mixed object files.
|
||||
# * Parallel build with -j<NCORES>, computed the same way the Makefile does.
|
||||
# * Uses the gtest-parallel checked in alongside this script (build_tools/),
|
||||
# so it works regardless of PATH.
|
||||
# * `set -euo pipefail` so any failure stops the script.
|
||||
#
|
||||
# Run from the repository root.
|
||||
#
|
||||
# Accepts one or more test binaries (gtest-parallel pools all their test cases
|
||||
# into one shared worker queue). The leading non-option arguments are treated as
|
||||
# binaries; everything from the first option onward is forwarded to
|
||||
# gtest-parallel / the test binaries.
|
||||
#
|
||||
# Example usage:
|
||||
# build_tools/rocksptest.sh db_test
|
||||
# build_tools/rocksptest.sh db_test -r1000 --gtest_filter=*MixedSlowdown*
|
||||
# build_tools/rocksptest.sh db_test env_test db_basic_test
|
||||
# build_tools/rocksptest.sh db_test env_test --gtest_filter=*Foo*
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
if [ "$#" -lt 1 ]; then
|
||||
echo "usage: $0 <test_binary> [more_test_binaries...] [gtest-parallel/test args...]" >&2
|
||||
echo "example: $0 db_test env_test -r1000 --gtest_filter=*MixedSlowdown*" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# First argument must be a test binary, not an option (catches a forgotten name).
|
||||
if [ "${1#-}" != "$1" ]; then
|
||||
echo "$0: first argument must be a test binary name, not an option ('$1')" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Collect the leading non-option arguments as test binaries; everything from the
|
||||
# first option onward is forwarded to gtest-parallel / the test binaries.
|
||||
BINS=()
|
||||
while [ "$#" -gt 0 ] && [ "${1#-}" = "$1" ]; do
|
||||
BINS+=("$1")
|
||||
shift
|
||||
done
|
||||
|
||||
# Paths as gtest-parallel expects them (relative to the current directory).
|
||||
BIN_PATHS=()
|
||||
for b in "${BINS[@]}"; do
|
||||
BIN_PATHS+=("./$b")
|
||||
done
|
||||
|
||||
# Directory of this script, so we use the gtest-parallel checked in next to it.
|
||||
SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
||||
|
||||
# Compute parallelism the same way the Makefile computes NCORES.
|
||||
NCORES=$(nproc 2>/dev/null || sysctl -n hw.ncpu 2>/dev/null || echo 4)
|
||||
|
||||
make AUTO_CLEAN=1 -j"$NCORES" "${BINS[@]}"
|
||||
"$SCRIPT_DIR/gtest-parallel" "${BIN_PATHS[@]}" "$@"
|
||||
@@ -1,76 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
# This source code is licensed under both the GPLv2 (found in the
|
||||
# COPYING file in the root directory) and Apache 2.0 License
|
||||
# (found in the LICENSE.Apache file in the root directory).
|
||||
#
|
||||
# Build a single RocksDB unit test binary and run it directly (serially).
|
||||
# Only recommended with --gtest_filter=... because of speed. See also
|
||||
# build_tools/rocksptest.sh
|
||||
#
|
||||
# Hardened version of a simple `make <bin> && ./<bin>` helper:
|
||||
# * AUTO_CLEAN=1 so the Makefile automatically runs a clean when the build
|
||||
# parameters (DEBUG_LEVEL, sanitizers, ASSERT_STATUS_CHECKED, RTTI, etc.)
|
||||
# have changed since the last build, preventing stale/mixed object files.
|
||||
# * Parallel build with -j<NCORES>, computed the same way the Makefile does.
|
||||
# * `set -euo pipefail` so any failure stops the script.
|
||||
#
|
||||
# Run from the repository root.
|
||||
#
|
||||
# Example usage:
|
||||
# build_tools/rockstest.sh db_test --gtest_filter=*MixedSlowdown*
|
||||
# build_tools/rockstest.sh env_test # Slow to run many tests serially
|
||||
#
|
||||
# Install mode:
|
||||
# build_tools/rockstest.sh install
|
||||
# Creates ~/bin/rockstest and ~/bin/rocksptest shims that defer to
|
||||
# build_tools/rockstest.sh / rocksptest.sh in whatever directory you run
|
||||
# them from, so you can just type `rockstest db_test` from any rocksdb
|
||||
# source root.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Install mode: write thin ~/bin shims that defer to the build_tools scripts in
|
||||
# the current directory.
|
||||
if [ "${1:-}" = "install" ]; then
|
||||
mkdir -p "$HOME/bin"
|
||||
for name in rockstest rocksptest; do
|
||||
dest="$HOME/bin/$name"
|
||||
cat > "$dest" <<EOF
|
||||
#!/usr/bin/env bash
|
||||
# Auto-generated by 'build_tools/rockstest.sh install'. Defers to
|
||||
# build_tools/$name.sh in the current rocksdb source root.
|
||||
if [ -x build_tools/$name.sh ]; then
|
||||
exec build_tools/$name.sh "\$@"
|
||||
else
|
||||
echo "build_tools/$name.sh not found (Not in a rocksdb source root directory?)" >&2
|
||||
exit 1
|
||||
fi
|
||||
EOF
|
||||
chmod +x "$dest"
|
||||
echo "Installed $dest"
|
||||
done
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ "$#" -lt 1 ]; then
|
||||
echo "usage: $0 <test_binary> [test args...]" >&2
|
||||
echo " $0 install # create ~/bin/rockstest and ~/bin/rocksptest shims" >&2
|
||||
echo "example: $0 db_test --gtest_filter=*MixedSlowdown*" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# First argument must be a test binary, not an option (catches a forgotten name).
|
||||
if [ "${1#-}" != "$1" ]; then
|
||||
echo "$0: first argument must be a test binary name, not an option ('$1')" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
BIN=$1
|
||||
shift
|
||||
|
||||
# Compute parallelism the same way the Makefile computes NCORES.
|
||||
NCORES=$(nproc 2>/dev/null || sysctl -n hw.ncpu 2>/dev/null || echo 4)
|
||||
|
||||
make AUTO_CLEAN=1 -j"$NCORES" "$BIN"
|
||||
./"$BIN" "$@"
|
||||
@@ -42,7 +42,7 @@ $RunOnly.Add("c_test") | Out-Null
|
||||
$RunOnly.Add("compact_on_deletion_collector_test") | Out-Null
|
||||
$RunOnly.Add("merge_test") | Out-Null
|
||||
$RunOnly.Add("stringappend_test") | Out-Null # Apparently incorrectly written
|
||||
$RunOnly.Add("backup_engine_test") | Out-Null # Disabled
|
||||
$RunOnly.Add("backupable_db_test") | Out-Null # Disabled
|
||||
$RunOnly.Add("timer_queue_test") | Out-Null # Not a gtest
|
||||
|
||||
if($RunAll -and $SuiteRun -ne "") {
|
||||
@@ -68,7 +68,7 @@ $BinariesFolder = -Join($RootFolder, "\build\Debug\")
|
||||
|
||||
if($WorkFolder -eq "") {
|
||||
|
||||
# If TEST_TMPDIR is set use it
|
||||
# If TEST_TMPDIR is set use it
|
||||
[string]$var = $Env:TEST_TMPDIR
|
||||
if($var -eq "") {
|
||||
$WorkFolder = -Join($RootFolder, "\db_tests\")
|
||||
@@ -93,7 +93,7 @@ $ExcludeCasesSet = New-Object System.Collections.Generic.HashSet[string]
|
||||
if($ExcludeCases -ne "") {
|
||||
Write-Host "ExcludeCases: $ExcludeCases"
|
||||
$l = $ExcludeCases -split ' '
|
||||
ForEach($t in $l) {
|
||||
ForEach($t in $l) {
|
||||
$ExcludeCasesSet.Add($t) | Out-Null
|
||||
}
|
||||
}
|
||||
@@ -102,7 +102,7 @@ $ExcludeExesSet = New-Object System.Collections.Generic.HashSet[string]
|
||||
if($ExcludeExes -ne "") {
|
||||
Write-Host "ExcludeExe: $ExcludeExes"
|
||||
$l = $ExcludeExes -split ' '
|
||||
ForEach($t in $l) {
|
||||
ForEach($t in $l) {
|
||||
$ExcludeExesSet.Add($t) | Out-Null
|
||||
}
|
||||
}
|
||||
@@ -118,10 +118,6 @@ if($ExcludeExes -ne "") {
|
||||
# MultiThreaded/MultiThreadedDBTest.
|
||||
# MultiThreaded/0 # GetParam() = 0
|
||||
# MultiThreaded/1 # GetParam() = 1
|
||||
# RibbonTypeParamTest/0. # TypeParam = struct DefaultTypesAndSettings
|
||||
# CompactnessAndBacktrackAndFpRate
|
||||
# Extremes
|
||||
# FindOccupancyForSuccessRate
|
||||
#
|
||||
# into this:
|
||||
#
|
||||
@@ -129,9 +125,6 @@ if($ExcludeExes -ne "") {
|
||||
# DBTest.WriteEmptyBatch
|
||||
# MultiThreaded/MultiThreadedDBTest.MultiThreaded/0
|
||||
# MultiThreaded/MultiThreadedDBTest.MultiThreaded/1
|
||||
# RibbonTypeParamTest/0.CompactnessAndBacktrackAndFpRate
|
||||
# RibbonTypeParamTest/0.Extremes
|
||||
# RibbonTypeParamTest/0.FindOccupancyForSuccessRate
|
||||
#
|
||||
# Output into the parameter in a form TestName -> Log File Name
|
||||
function ExtractTestCases([string]$GTestExe, $HashTable) {
|
||||
@@ -145,8 +138,6 @@ function ExtractTestCases([string]$GTestExe, $HashTable) {
|
||||
|
||||
ForEach( $l in $Tests) {
|
||||
|
||||
# remove trailing comment if any
|
||||
$l = $l -replace '\s+\#.*',''
|
||||
# Leading whitespace is fine
|
||||
$l = $l -replace '^\s+',''
|
||||
# Trailing dot is a test group but no whitespace
|
||||
@@ -155,7 +146,8 @@ function ExtractTestCases([string]$GTestExe, $HashTable) {
|
||||
} else {
|
||||
# Otherwise it is a test name, remove leading space
|
||||
$test = $l
|
||||
# create a log name
|
||||
# remove trailing comment if any and create a log name
|
||||
$test = $test -replace '\s+\#.*',''
|
||||
$test = "$Group$test"
|
||||
|
||||
if($ExcludeCasesSet.Contains($test)) {
|
||||
@@ -261,7 +253,7 @@ if($Run -ne "") {
|
||||
|
||||
$DiscoveredExe = @()
|
||||
dir -Path $search_path | ForEach-Object {
|
||||
$DiscoveredExe += ($_.Name)
|
||||
$DiscoveredExe += ($_.Name)
|
||||
}
|
||||
|
||||
# Remove exclusions
|
||||
@@ -301,7 +293,7 @@ if($SuiteRun -ne "") {
|
||||
|
||||
$ListOfExe = @()
|
||||
dir -Path $search_path | ForEach-Object {
|
||||
$ListOfExe += ($_.Name)
|
||||
$ListOfExe += ($_.Name)
|
||||
}
|
||||
|
||||
# Exclude those in RunOnly from running as suites
|
||||
@@ -356,7 +348,7 @@ function RunJobs($Suites, $TestCmds, [int]$ConcurrencyVal)
|
||||
|
||||
# Wait for all to finish and get the results
|
||||
while(($JobToLog.Count -gt 0) -or
|
||||
($TestCmds.Count -gt 0) -or
|
||||
($TestCmds.Count -gt 0) -or
|
||||
($Suites.Count -gt 0)) {
|
||||
|
||||
# Make sure we have maximum concurrent jobs running if anything
|
||||
@@ -476,8 +468,8 @@ RunJobs -Suites $CasesToRun -TestCmds $TestExes -ConcurrencyVal $Concurrency
|
||||
|
||||
$EndDate = (Get-Date)
|
||||
|
||||
New-TimeSpan -Start $StartDate -End $EndDate |
|
||||
ForEach-Object {
|
||||
New-TimeSpan -Start $StartDate -End $EndDate |
|
||||
ForEach-Object {
|
||||
"Elapsed time: {0:g}" -f $_
|
||||
}
|
||||
|
||||
@@ -491,3 +483,5 @@ if(!$script:success) {
|
||||
}
|
||||
|
||||
exit 0
|
||||
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
#!/bin/bash
|
||||
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
|
||||
set -ex
|
||||
set -e
|
||||
|
||||
ROCKSDB_VERSION="6.7.3"
|
||||
ZSTD_VERSION="1.4.4"
|
||||
ROCKSDB_VERSION="5.10.3"
|
||||
ZSTD_VERSION="1.1.3"
|
||||
|
||||
echo "This script configures CentOS with everything needed to build and run RocksDB"
|
||||
|
||||
@@ -40,6 +40,5 @@ cd /usr/local/rocksdb
|
||||
chown -R vagrant:vagrant /usr/local/rocksdb/
|
||||
sudo -u vagrant make static_lib
|
||||
cd examples/
|
||||
sudo -u vagrant LD_LIBRARY_PATH=/usr/local/lib/ make all
|
||||
sudo -u vagrant LD_LIBRARY_PATH=/usr/local/lib/ ./c_simple_example
|
||||
|
||||
sudo -u vagrant make all
|
||||
sudo -u vagrant ./c_simple_example
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user