diff --git a/ci/publish-bpf-sdk.sh b/ci/publish-bpf-sdk.sh index 3ef465ecc..dec67055a 100755 --- a/ci/publish-bpf-sdk.sh +++ b/ci/publish-bpf-sdk.sh @@ -3,9 +3,7 @@ set -e cd "$(dirname "$0")/.." -version=$(./ci/crate-version.sh) eval "$(ci/channel-info.sh)" - if [[ $BUILDKITE_BRANCH = "$STABLE_CHANNEL" ]]; then CHANNEL=stable elif [[ $BUILDKITE_BRANCH = "$EDGE_CHANNEL" ]]; then @@ -17,19 +15,10 @@ fi echo --- Creating tarball ( set -x - rm -rf bpf-sdk/ - mkdir bpf-sdk/ - ( - echo "$version" - git rev-parse HEAD - ) > bpf-sdk/version.txt - - cp -ra programs/bpf/c/sdk/* bpf-sdk/ - - tar jvcf bpf-sdk.tar.bz2 bpf-sdk/ + programs/bpf/c/sdk/scripts/package.sh + [[ -f bpf-sdk.tar.bz2 ]] ) - echo --- AWS S3 Store if [[ -z $CHANNEL ]]; then echo Skipped @@ -48,4 +37,3 @@ else fi exit 0 - diff --git a/programs/bpf/c/makefile b/programs/bpf/c/makefile index 31e5bcfd0..5c99cdec1 100644 --- a/programs/bpf/c/makefile +++ b/programs/bpf/c/makefile @@ -1 +1,2 @@ +$(shell ./sdk/scripts/install.sh) include sdk/bpf.mk diff --git a/programs/bpf/c/sdk/.gitignore b/programs/bpf/c/sdk/.gitignore new file mode 100644 index 000000000..74eadd225 --- /dev/null +++ b/programs/bpf/c/sdk/.gitignore @@ -0,0 +1 @@ +/criterion-* diff --git a/programs/bpf/c/sdk/README.md b/programs/bpf/c/sdk/README.md index 95395cfae..b9f2ca180 100644 --- a/programs/bpf/c/sdk/README.md +++ b/programs/bpf/c/sdk/README.md @@ -65,6 +65,20 @@ bool entrypoint(const uint8_t *input) { Then run `make` to build `out/program.o`. Run `make help` for more details. +### Unit tests +Built-in support for unit testing is provided by the +[Criterion](https://criterion.readthedocs.io/en/master/index.html) test framework. +To get started create the file `test/example.c` containing: +```c +#include +#include "../src/program.c" + +Test(test_suite_name, test_case_name) { + cr_assert(true); +} +``` +Then run `make test`. + ### Limitations * Programs must be fully contained within a single .c file * No libc is available but `solana_sdk.h` provides a minimal set of diff --git a/programs/bpf/c/sdk/bpf.mk b/programs/bpf/c/sdk/bpf.mk index 771e5307e..d803487ac 100644 --- a/programs/bpf/c/sdk/bpf.mk +++ b/programs/bpf/c/sdk/bpf.mk @@ -1,3 +1,4 @@ +LOCAL_PATH := $(dir $(lastword $(MAKEFILE_LIST))) all: .PHONY: help all clean @@ -8,6 +9,7 @@ endif INC_DIRS ?= SRC_DIR ?= ./src +TEST_DIR ?= ./test OUT_DIR ?= ./out OS=$(shell uname) @@ -27,21 +29,32 @@ LLC := llc-7 OBJ_DUMP := llvm-objdump-7 endif -SYSTEM_INC_DIRS := -isystem $(dir $(lastword $(MAKEFILE_LIST)))inc +SYSTEM_INC_DIRS := -isystem $(LOCAL_PATH)inc -CC_FLAGS := \ + +C_FLAGS := \ -Werror \ - -target bpf \ -O2 \ - -emit-llvm \ -fno-builtin \ -std=c17 \ + $(SYSTEM_INC_DIRS) \ + $(addprefix -I,$(INC_DIRS)) CXX_FLAGS := \ - $(CC_FLAGS) \ + $(C_FLAGS) \ -std=c++17 \ -LLC_FLAGS := \ +BPF_C_FLAGS := \ + $(C_FLAGS) \ + -emit-llvm \ + -target bpf \ + +BPF_CXX_FLAGS := \ + $(CXX_FLAGS) \ + -emit-llvm \ + -target bpf \ + +BPF_LLC_FLAGS := \ -march=bpf \ -filetype=obj \ @@ -50,6 +63,19 @@ OBJ_DUMP_FLAGS := \ -source \ -disassemble \ +TESTFRAMEWORK_FLAGS := \ + -isystem $(LOCAL_PATH)criterion-v2.3.2/include \ + -L $(LOCAL_PATH)criterion-v2.3.2/lib \ + -lcriterion \ + +TEST_C_FLAGS := \ + $(C_FLAGS) \ + $(TESTFRAMEWORK_FLAGS) \ + +TEST_CXX_FLAGS := \ + $(CXX_FLAGS) \ + $(TESTFRAMEWORK_FLAGS) \ + help: @echo 'BPF Program makefile' @echo '' @@ -59,6 +85,8 @@ help: @echo ' - Programs are a single .c or .cc source file (may include headers)' @echo ' - Programs are located in the source directory: $(SRC_DIR)' @echo ' - Programs are named by their basename (eg. file name:foo.c/foo.cc -> program name:foo)' + @echo ' - Tests are located in the test directory: $(TEST_DIR)' + @echo ' - Tests are named by their basename (eg. file name:foo.c/foo.cc -> test name:test_foo)' @echo ' - Output files will be placed in the directory: $(OUT_DIR)' @echo '' @echo 'User settings' @@ -71,6 +99,8 @@ help: @echo ' SYSTEM_INC_DIRS=$(SYSTEM_INC_DIRS)' @echo ' - Location of source files:' @echo ' SRC_DIR=$(SRC_DIR)' + @echo ' - Location of test files:' + @echo ' TEST_DIR=$(TEST_DIR)' @echo ' - Location to place output files:' @echo ' OUT_DIR=$(OUT_DIR)' @echo ' - Location of LLVM:' @@ -78,13 +108,15 @@ help: @echo '' @echo 'Usage:' @echo ' - make help - This help message' - @echo ' - make all - Builds all the programs in the directory: $(SRC_DIR)' - @echo ' - make clean - Cleans all programs' + @echo ' - make all - Builds all the programs' + @echo ' - make test - Build and run all tests' @echo ' - make dump_ - Dumps the contents of the program to stdout' @echo ' - make - Build a single program by name' @echo '' @echo 'Available programs:' $(foreach name, $(PROGRAM_NAMES), @echo ' - $(name)'$(\n)) + @echo 'Available tests:' + $(foreach name, $(TEST_NAMES), @echo ' - $(name)'$(\n)) @echo '' @echo 'Example:' @echo ' - Assuming a programed named foo (src/foo.c)' @@ -95,21 +127,32 @@ help: $(OUT_DIR)/%.bc: $(SRC_DIR)/%.c @echo "[cc] $@ ($<)" $(_@)mkdir -p $(OUT_DIR) - $(_@)$(CC) $(CC_FLAGS) $(SYSTEM_INC_DIRS) $(addprefix -I,$(INC_DIRS)) -o $@ -c $< -MD -MF $(@:.bc=.d) + $(_@)$(CC) $(BPF_C_FLAGS) -o $@ -c $< -MD -MF $(@:.bc=.d) $(OUT_DIR)/%.bc: $(SRC_DIR)/%.cc @echo "[cc] $@ ($<)" $(_@)mkdir -p $(OUT_DIR) - $(_@)$(CXX) $(CXX_FLAGS) $(SYSTEM_INC_DIRS) $(addprefix -I,$(INC_DIRS)) -o $@ -c $< -MD -MF $(@:.bc=.d) + $(_@)$(CXX) $(BPF_CXX_FLAGS) -o $@ -c $< -MD -MF $(@:.bc=.d) .PRECIOUS: $(OUT_DIR)/%.o $(OUT_DIR)/%.o: $(OUT_DIR)/%.bc @echo "[llc] $@ ($<)" - $(_@)$(LLC) $(LLC_FLAGS) -o $@ $< + $(_@)$(LLC) $(BPF_LLC_FLAGS) -o $@ $< + +$(OUT_DIR)/test_%: $(TEST_DIR)/%.c + @echo "[test cc] $@ ($<)" + $(_@)mkdir -p $(OUT_DIR) + $(_@)$(CC) $(TEST_C_FLAGS) -o $@ $< -MD -MF $(@:=.d) + +$(OUT_DIR)/test_%: $(TEST_DIR)/%.cc + @echo "[test cc] $@ ($<)" + $(_@)mkdir -p $(OUT_DIR) + $(_@)$(CXX) $(TEST_CXX_FLAGS) -o $@ $< -MD -MF $(@:=.d) -include $(wildcard $(OUT_DIR)/*.d) PROGRAM_NAMES := $(notdir $(basename $(wildcard $(SRC_DIR)/*.c $(SRC_DIR)/*.cc))) +TEST_NAMES := $(addprefix test_,$(notdir $(basename $(wildcard $(TEST_DIR)/*.c)))) define \n @@ -118,7 +161,11 @@ endef all: $(PROGRAM_NAMES) -%: $(addprefix $(OUT_DIR)/, %.o) ; +test: $(TEST_NAMES) + $(foreach test, $(TEST_NAMES), $(OUT_DIR)/$(test)$(\n)) + +$(PROGRAM_NAMES): %: $(addprefix $(OUT_DIR)/, %.o) ; +$(TEST_NAMES): %: $(addprefix $(OUT_DIR)/, %) ; dump_%: % $(_@)$(OBJ_DUMP) $(OBJ_DUMP_FLAGS) $(addprefix $(OUT_DIR)/, $(addsuffix .o, $<)) diff --git a/programs/bpf/c/sdk/scripts/install.sh b/programs/bpf/c/sdk/scripts/install.sh new file mode 100755 index 000000000..3cde7f11c --- /dev/null +++ b/programs/bpf/c/sdk/scripts/install.sh @@ -0,0 +1,23 @@ +#!/usr/bin/env bash + +cd "$(dirname "$0")"/.. + +# Install Criterion for all supported platforms +version=v2.3.2 +( + [[ ! -d criterion-$version ]] || exit 0 + set -ex + wget https://github.com/Snaipe/Criterion/releases/download/$version/criterion-$version-osx-x86_64.tar.bz2 + wget https://github.com/Snaipe/Criterion/releases/download/$version/criterion-$version-linux-x86_64.tar.bz2 + tar jxf criterion-$version-osx-x86_64.tar.bz2 + tar jxf criterion-$version-linux-x86_64.tar.bz2 + rm -rf criterion-$version-osx-x86_64.tar.bz2 criterion-$version-linux-x86_64.tar.bz2 + + [[ ! -f criterion-$version/README.md ]] + echo "https://github.com/Snaipe/Criterion/releases/tag/$version" > criterion-$version/README.md +) +# shellcheck disable=SC2181 +if [[ $? -ne 0 ]]; then + rm -rf criterion-$version* + exit 1 +fi diff --git a/programs/bpf/c/sdk/scripts/package.sh b/programs/bpf/c/sdk/scripts/package.sh new file mode 100755 index 000000000..4e6e3fa1a --- /dev/null +++ b/programs/bpf/c/sdk/scripts/package.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env bash +set -ex + +SOLANA_ROOT="$(cd "$(dirname "$0")"/../../../../..; pwd)" +[[ -f "$SOLANA_ROOT"/LICENSE && -d "$SOLANA_ROOT"/ci ]] + +rm -rf bpf-sdk/ +mkdir bpf-sdk/ + +( + "$SOLANA_ROOT"/ci/crate-version.sh + git rev-parse HEAD +) > bpf-sdk/version.txt + +"$SOLANA_ROOT"/programs/bpf/c/sdk/scripts/install.sh +cp -ra "$SOLANA_ROOT"/programs/bpf/c/sdk/* bpf-sdk/ +rm -rf bpf-sdk/scripts/ + +tar jvcf bpf-sdk.tar.bz2 bpf-sdk/ diff --git a/programs/bpf/c/test/bench_alu.c b/programs/bpf/c/test/bench_alu.c new file mode 100644 index 000000000..52a2050f9 --- /dev/null +++ b/programs/bpf/c/test/bench_alu.c @@ -0,0 +1,11 @@ +#include +#include "../src/bench_alu.c" + +Test(bench_alu, sanity) { + uint64_t input[] = {500, 0}; + + cr_assert(entrypoint((uint8_t *) input)); + + cr_assert_eq(input[0], 500); + cr_assert_eq(input[1], 5); +}