Skip to content

Commit bae8a83

Browse files
sbgaiatanneberger
andauthored
Optimize LF test compilation with CMake (#346)
* Move federated LF tests to test/lf/src/federated * Add federated LF tests in Makefile * Add LF test coverage and avoid rebuilding reactor-uc * Fix ASAN errors in connection logging and event payload pool test * Enhance runtime symlink handling in UcPlatformGenerator * Add reusable LF test definitions and discovery functions * Cleanup unused test definition and discovery functions * Refactor CMake build to support independent unit and LF test/coverage builds. * Add batch processing function in lfc.cmake to optimize LFC compilation * Fix unit and LF test build configurations in Makefile * Refactor LF test CMake * Add unit-test and unit-test-lf-test to PHONY targets in Makefile * Add environment variable for REACTOR_UC_PATH in coverage workflow * Remove redundant path from include statements in legacy LF files * Merge unit and LF tests into a single CI step * Fix argument order in LFC command for single and batch runs * Restore no-compile (-n) flag support in uC generator * Revert include path * Fix legacy LF test builds by adding source dir to include pathAdd * Move federated LF tests to test/lf/src/federated * Add federated LF tests in Makefile * Add LF test coverage and avoid rebuilding reactor-uc * Fix ASAN errors in connection logging and event payload pool test * Enhance runtime symlink handling in UcPlatformGenerator * Add reusable LF test definitions and discovery functions * Cleanup unused test definition and discovery functions * Refactor CMake build to support independent unit and LF test/coverage builds. * Add batch processing function in lfc.cmake to optimize LFC compilation * Fix unit and LF test build configurations in Makefile * Refactor LF test CMake * Add unit-test and unit-test-lf-test to PHONY targets in Makefile * Add environment variable for REACTOR_UC_PATH in coverage workflow * Remove redundant path from include statements in legacy LF files * Merge unit and LF tests into a single CI step * Fix argument order in LFC command for single and batch runs * Restore no-compile (-n) flag support in uC generator * Revert include path * Fix legacy LF test builds by adding source dir to include pathAdd * Simplify test/coverage.cmake with helper-function API * Restore LF_DEBUG in Connection_register_downstream * Refactor CI and Makefile to remove parallel execution in test commands * Fix coverage configuration * Suppress lcov unused pattern errors * Merge lf_run_lfc and lf_run_lfc_batch functions in lfc.cmake --------- Co-authored-by: tanneberger <github@tanneberger.me>
1 parent 4ffade0 commit bae8a83

44 files changed

Lines changed: 450 additions & 145 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
name: Set up build environment
2-
description: Set up Java and Gradle.
2+
description: Set up Java and cache the compiled LFC + Gradle dependencies.
33

44
runs:
55
using: "composite"
66
steps:
7-
- uses: actions/setup-java@v3
7+
- uses: actions/setup-java@v4
88
with:
99
distribution: temurin
1010
java-version: 17
@@ -13,5 +13,14 @@ runs:
1313
with:
1414
path: lfc/core/.antlr-generator-3.2.0-patch.jar
1515
key: antlr-generator-3.2.0-patch
16-
- name: Gradle Build Action
17-
uses: gradle/gradle-build-action@v2.8.0
16+
- name: Cache LFC build + Gradle
17+
uses: actions/cache@v4
18+
with:
19+
path: |
20+
lfc/build
21+
lfc/**/build
22+
~/.gradle/caches
23+
~/.gradle/wrapper
24+
key: lfc-${{ runner.os }}-${{ hashFiles('lfc/**/*.kt', 'lfc/**/*.java', 'lfc/**/*.gradle*', 'lfc/gradle/wrapper/**') }}
25+
restore-keys: |
26+
lfc-${{ runner.os }}-
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
name: Set up LF test environment
2+
description: Restore (or populate) the LFC generated sources cache. This avoids the LFC generation step when the test files and the LFC files haven't changed, which can save a significant amount of time in the CI workflow.
3+
4+
outputs:
5+
cache-hit:
6+
description: Whether the src-gen cache was restored from a prior run.
7+
value: ${{ steps.lf-srcgen-cache.outputs.cache-hit }}
8+
9+
runs:
10+
using: "composite"
11+
steps:
12+
- name: Cache generated LF sources
13+
id: lf-srcgen-cache
14+
uses: actions/cache@v4
15+
with:
16+
path: test/lf/src-gen
17+
key: lf-srcgen-${{ runner.os }}-${{ hashFiles('.gitmodules', 'lfc/**/*.kt', 'lfc/**/*.java', 'lfc/**/*.gradle*', 'test/lf/src/**/*.lf', 'cmake/lfc.cmake', 'test/lf/lf_tests.cmake', 'test/lf/lf_test_functions.cmake') }}
18+
19+
- uses: ./.github/actions/lingua-franca
20+
if: steps.lf-srcgen-cache.outputs.cache-hit != 'true'
21+
22+
- name: Generate LF sources
23+
if: steps.lf-srcgen-cache.outputs.cache-hit != 'true'
24+
shell: bash
25+
env:
26+
REACTOR_UC_PATH: ${{ github.workspace }}
27+
run: make lf-generate

.github/workflows/ci.yml

Lines changed: 35 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -7,42 +7,50 @@ on:
77

88
permissions:
99
contents: read
10+
issues: write
1011
pull-requests: write
1112

13+
env:
14+
REACTOR_UC_PATH: ${{ github.workspace }}
15+
1216
jobs:
13-
ci:
14-
strategy:
15-
matrix:
16-
platform: [ubuntu-24.04, macos-latest]
17-
name: ci
18-
env:
19-
REACTOR_UC_PATH: ${{ github.workspace }}
20-
runs-on: ${{ matrix.platform }}
17+
coverage:
18+
name: coverage
19+
runs-on: ubuntu-24.04
2120
steps:
22-
- name: Checkout
23-
uses: actions/checkout@v3
21+
- uses: actions/checkout@v4
2422
with:
25-
submodules: recursive
26-
- name: Install deps (Linux)
23+
submodules: recursive
24+
25+
- name: Install deps
2726
run: |
28-
sudo apt-get install pipx -y
27+
sudo apt-get install -y pipx lcov
2928
sudo pipx install clang-tidy
30-
if: matrix.platform == 'ubuntu-24.04'
3129
32-
- name: Install deps (macOS)
33-
run: brew install coreutils
34-
if: matrix.platform == 'macos-latest'
30+
- uses: ./.github/actions/setup-lf-tests
3531

36-
- name: Setup java and gradle for compiling lfc
37-
uses: ./.github/actions/lingua-franca
32+
- name: Run coverage (also runs tests)
33+
run: make coverage CMAKE_EXTRA_FLAGS=-DLF_SKIP_GENERATE=ON
34+
35+
- name: Publish coverage results
36+
uses: romeovs/lcov-reporter-action@2a28ec3e25fb7eae9cb537e9141603486f810d1a
37+
with:
38+
lcov-file: build/coverage.info
39+
delete-old-comments: true
40+
github-token: ${{ secrets.GITHUB_TOKEN }}
3841

39-
# Uncomment to SSH into the runner.
40-
# - name: Setup upterm session
41-
# uses: lhotari/action-upterm@v1
42-
# if: matrix.platform == 'macos-latest'
42+
macos-build:
43+
name: macos build
44+
runs-on: macos-latest
45+
steps:
46+
- uses: actions/checkout@v4
47+
with:
48+
submodules: recursive
49+
50+
- name: Install deps
51+
run: brew install coreutils
4352

44-
- name: Run unit tests
45-
run: make unit-test
53+
- uses: ./.github/actions/setup-lf-tests
4654

47-
- name: Run LF integration tests
48-
run: make lf-test
55+
- name: Unit + LF tests
56+
run: make unit-test-lf-test CMAKE_EXTRA_FLAGS=-DLF_SKIP_GENERATE=ON

.github/workflows/coverage.yml

Lines changed: 0 additions & 36 deletions
This file was deleted.

CMakeLists.txt

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,12 @@ project(reactor-uc LANGUAGES C)
55
set(BUILD_TESTS OFF CACHE BOOL "Build all tests")
66
set(BUILD_LF_TESTS OFF CACHE BOOL "Build lf tests")
77
set(BUILD_UNIT_TESTS OFF CACHE BOOL "Build unit tests")
8-
set(TEST_COVERAGE OFF CACHE BOOL "Compute test coverage")
98
set(ASAN OFF CACHE BOOL "Compile with AddressSanitizer")
109
set(PLATFORM "POSIX" CACHE STRING "Platform to target")
1110
set(SCHEDULER "DYNAMIC" CACHE STRING "Scheduler to use")
1211
set(NETWORK_CHANNEL_TCP_POSIX OFF CACHE BOOL "Use POSIX TCP NetworkChannel")
1312
set(FEDERATED OFF CACHE BOOL "Compile with federated sources")
1413

15-
# Code coverage setup
16-
if(TEST_COVERAGE)
17-
set(CMAKE_BUILD_TYPE "Debug")
18-
include(external/cmake/CodeCoverage.cmake)
19-
append_coverage_compiler_flags()
20-
add_compile_options(-fprofile-update=atomic)
21-
endif()
2214

2315
# Setup AddressSanitizer for chasing memory bugs.
2416
if(ASAN)
@@ -30,8 +22,12 @@ endif()
3022
if(BUILD_TESTS)
3123
set(BUILD_LF_TESTS ON)
3224
set(BUILD_UNIT_TESTS ON)
25+
endif()
26+
27+
if(BUILD_UNIT_TESTS OR BUILD_LF_TESTS)
3328
set(NETWORK_CHANNEL_TCP_POSIX ON) # TODO: This is currently needed because one of the tests uses this stack, we need a nicer way of selecting build options for tests and apps.
3429
set(FEDERATED ON)
30+
set(LFC_RUNTIME_SYMLINK ON)
3531
set(CMAKE_BUILD_TYPE "Debug")
3632
find_program(CLANG_TIDY clang-tidy)
3733
if (CLANG_TIDY)
@@ -110,11 +106,8 @@ endif()
110106
add_compile_options (-fdiagnostics-color=always)
111107
target_include_directories(reactor-uc PUBLIC ${CMAKE_CURRENT_LIST_DIR}/include ${CMAKE_CURRENT_LIST_DIR}/external)
112108

113-
if(BUILD_UNIT_TESTS)
114-
set(UNITY_DIR ${CMAKE_CURRENT_LIST_DIR}/external/Unity)
109+
110+
if(BUILD_UNIT_TESTS OR BUILD_LF_TESTS)
115111
include(CTest)
116-
add_library(Unity STATIC ${UNITY_DIR}/src/unity.c)
117-
target_include_directories(Unity PUBLIC ${UNITY_DIR}/src)
118-
set_target_properties( Unity PROPERTIES C_CLANG_TIDY "") # Disable clang-tidy for this external lib.
119-
add_subdirectory(test/unit)
112+
add_subdirectory(test)
120113
endif()

Makefile

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
1-
.PHONY: clean test coverage asan format format-check ci lf-test lib proto docs platform-test examples complexity
1+
.PHONY: clean test coverage asan format format-check ci lf-test lib proto docs platform-test examples complexity unit-test unit-test-lf-test lf-generate
22

3-
test: unit-test lf-test platform-test examples
3+
# Extra flags forwarded to every cmake configure (e.g. -DLF_SKIP_GENERATE=ON from CI)
4+
CMAKE_EXTRA_FLAGS ?=
5+
6+
test: unit-test-lf-test platform-test examples
47

58
# Generate protobuf code
69
proto:
@@ -12,15 +15,27 @@ lib:
1215
cmake --build build
1316
make -C build
1417

18+
# Build and run the unit and LF tests
19+
unit-test-lf-test:
20+
cmake -Bbuild -DBUILD_TESTS=ON $(CMAKE_EXTRA_FLAGS)
21+
cmake --build build
22+
cd build && ctest --output-on-failure
23+
1524
# Build and run the unit tests
1625
unit-test:
17-
cmake -Bbuild -DBUILD_TESTS=ON
26+
cmake -Bbuild -DBUILD_UNIT_TESTS=ON -DBUILD_LF_TESTS=OFF $(CMAKE_EXTRA_FLAGS)
1827
cmake --build build
1928
cd build && ctest --output-on-failure
2029

21-
# Build and run lf tests
30+
# Build and run the LF tests
2231
lf-test:
23-
make -C test/lf
32+
cmake -Bbuild -DBUILD_LF_TESTS=ON -DBUILD_UNIT_TESTS=OFF $(CMAKE_EXTRA_FLAGS)
33+
cmake --build build
34+
cd build && ctest --output-on-failure
35+
36+
# Configure-only: populate src-gen by running LFC on the LF tests, no C compilation.
37+
lf-generate:
38+
cmake -Bbuild -DBUILD_LF_TESTS=ON -DBUILD_UNIT_TESTS=OFF $(CMAKE_EXTRA_FLAGS)
2439

2540
platform-test:
2641
cd test/platform && ./runAll.sh
@@ -30,13 +45,13 @@ examples:
3045

3146
# Get coverage data on unit tests
3247
coverage:
33-
cmake -Bbuild -DBUILD_TESTS=ON -DTEST_COVERAGE=ON
48+
cmake -Bbuild -DBUILD_TESTS=ON -DTEST_COVERAGE=ON $(CMAKE_EXTRA_FLAGS)
3449
cmake --build build
3550
make coverage -C build
3651

3752
# Compile tests with AddressSanitizer and run them
3853
asan:
39-
cmake -Bbuild -DASAN=ON -DBUILD_TESTS=ON
54+
cmake -Bbuild -DASAN=ON -DBUILD_TESTS=ON $(CMAKE_EXTRA_FLAGS)
4055
cmake --build build
4156
make test -C build
4257

@@ -57,7 +72,7 @@ format-check:
5772
ci: clean format test coverage
5873

5974
clean:
60-
rm -rf build test/lf/src-gen test/lf/bin
75+
rm -rf build src-gen test/lf/src-gen test/lf/bin
6176

6277
complexity:
6378
complexity --histogram --score --thresh=2 $(SRC_FILES)

cmake/lfc.cmake

Lines changed: 34 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
cmake_minimum_required(VERSION 3.20.0)
22

3+
set(LFC_RUNTIME_SYMLINK OFF CACHE BOOL "Use a symlink to the runtime instead of copying it into src-gen")
4+
35
# Sets up default values for LF_MAIN and LOG_LEVEL which
46
# can be overridden by the user, either in the main CMakeLists.txt or
57
# from the command line.
@@ -13,35 +15,43 @@ function (lf_setup)
1315
endif()
1416
endfunction()
1517

16-
# Run the LFC compiler on the specified LF source file, LF_SOURCE_DIR/LF_MAIN.lf. Also make the CMake configuration
17-
# depend on any LF source file found within LF_SOURCE_DIR. This ensures that the LFC compiler is rerun whenever any LF
18-
# source file changes.
19-
# Args:
20-
# LF_SOURCE_DIR: The directory containing the LF source file.
21-
# LF_MAIN: The name of the LF source file without the .lf extension.
22-
function(lf_run_lfc LF_SOURCE_DIR LF_MAIN)
23-
# Check if the LF_SOURCE_DIR exists
24-
if (NOT EXISTS ${LF_SOURCE_DIR})
25-
message(FATAL_ERROR "LF source directory does not exist: ${LF_SOURCE_DIR}")
18+
# Run the LFC compiler on one or more LF source files in a single invocation.
19+
# Batching avoids paying JVM startup cost per file when possible.
20+
# Args (keyword form):
21+
# FILES <path>... Absolute paths to one or more .lf files. Required.
22+
# OUTPUT_DIR <path> Root output directory for generated code. Defaults
23+
# to CMAKE_CURRENT_SOURCE_DIR when omitted.
24+
function(lf_run_lfc)
25+
cmake_parse_arguments(LFC "" "OUTPUT_DIR" "FILES" ${ARGN})
26+
27+
if(NOT LFC_FILES)
28+
message(FATAL_ERROR "lf_run_lfc: FILES is required")
29+
endif()
30+
if(NOT LFC_OUTPUT_DIR)
31+
set(LFC_OUTPUT_DIR ${CMAKE_CURRENT_SOURCE_DIR})
2632
endif()
2733

28-
# Check if the LF_MAIN file exists
29-
if (NOT EXISTS ${LF_SOURCE_DIR}/${LF_MAIN}.lf)
30-
message(FATAL_ERROR "LF main file does not exist: ${LF_SOURCE_DIR}/${LF_MAIN}.lf")
31-
endif()
34+
foreach(_F ${LFC_FILES})
35+
if(NOT EXISTS ${_F})
36+
message(FATAL_ERROR "LF source file does not exist: ${_F}")
37+
endif()
38+
endforeach()
3239

33-
set(LFC_COMMAND $ENV{REACTOR_UC_PATH}/lfc/bin/lfc-dev ${LF_SOURCE_DIR}/${LF_MAIN}.lf -n -o ${CMAKE_CURRENT_SOURCE_DIR})
34-
execute_process(COMMAND echo "Running LFC: ${LFC_COMMAND}")
35-
execute_process(
36-
COMMAND ${LFC_COMMAND}
37-
ECHO_OUTPUT_VARIABLE
38-
COMMAND_ERROR_IS_FATAL ANY
40+
set(LFC_COMMAND $ENV{REACTOR_UC_PATH}/lfc/bin/lfc-dev -n -o ${LFC_OUTPUT_DIR} ${LFC_FILES})
41+
if(LFC_RUNTIME_SYMLINK)
42+
list(APPEND LFC_COMMAND --runtime-symlink)
43+
endif()
3944

45+
list(LENGTH LFC_FILES _num_files)
46+
message(STATUS "Running LFC on ${_num_files} file(s)")
47+
execute_process(
48+
COMMAND ${LFC_COMMAND}
49+
ECHO_OUTPUT_VARIABLE
50+
COMMAND_ERROR_IS_FATAL ANY
4051
)
41-
file(GLOB_RECURSE LF_SOURCES ${LF_SOURCE_DIR}/*.lf)
42-
set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ${LF_SOURCES})
43-
message(STATUS "Found LF sources: ${LF_SOURCES}")
44-
52+
53+
# Make CMake reconfigure whenever any of the input files change
54+
set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ${LFC_FILES})
4555
endfunction()
4656

4757
# Build the generated code from the LFC compiler. This function should be called after lf_run_lfc.

examples/fed-template/src/MyFed.lf

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,10 @@ reactor Dest {
1919

2020

2121
federated reactor {
22-
@platform_native
22+
@platform("native")
2323
src = new Src()
2424

25-
@platform_zephyr
25+
@platform("zephyr")
2626
dest = new Dest()
2727

2828
src.out -> dest.in

lfc/cli/lfc/src/main/java/org/lflang/cli/Lfc.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,7 @@ public GeneratorArguments getArgs() {
291291
hierarchicalBin,
292292
getJsonObject(),
293293
lint,
294+
noCompile != null && noCompile,
294295
quiet,
295296
getRtiUri(),
296297
genFedTemplates != null,

0 commit comments

Comments
 (0)