diff --git a/.appveyor.yml b/.appveyor.yml index e6b034c..c52387c 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -37,6 +37,7 @@ skip_commits: - '**/*.html' - '**/*.md' - '.travis.yml' + - '.github/workflows/*' #---------------------------------# # additional packages # diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml new file mode 100644 index 0000000..1411fc9 --- /dev/null +++ b/.github/workflows/build-and-test.yml @@ -0,0 +1,156 @@ +name: ci-scripts build/test + +on: + push: + branches: [ devel/gh-actions ] + +env: + SETUP_PATH: .:.ci + SET: test01 + VV: 1 + BASE_RECURSIVE: NO + CMP: gcc + APT: re2c + CHOCO: re2c + BREW: re2c + +jobs: + unit-test: + name: Unit tests on ${{ matrix.os }} + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-20.04, ubuntu-18.04, ubuntu-16.04, windows-2019, windows-2016, macos-10.15] + steps: + - uses: actions/checkout@v2 + - name: Show initial environment + run: python cue-test.py env + - name: Run unit tests + run: python cue-test.py + + build-linux: + name: ${{ matrix.cmp }} / ${{ matrix.configuration }} / ${{ matrix.os }} + runs-on: ${{ matrix.os }} + env: + CMP: ${{ matrix.cmp }} + BCFG: ${{ matrix.configuration }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-20.04, ubuntu-18.04, ubuntu-16.04] + cmp: [gcc, clang] + configuration: [default, static, debug, static-debug] + steps: + - uses: actions/checkout@v2 + - name: Prepare and compile dependencies + run: python cue.py prepare + - name: Build main module (example app) + run: python cue.py build + - name: Run main module tests + run: python cue.py test + - name: Collect and show test results + run: python cue.py test-results + + build-macos: + name: ${{ matrix.cmp }} / ${{ matrix.configuration }} / ${{ matrix.os }} + runs-on: ${{ matrix.os }} + env: + CMP: ${{ matrix.cmp }} + BCFG: ${{ matrix.configuration }} + strategy: + fail-fast: false + matrix: + os: [macos-10.15] + cmp: [clang] + configuration: [default, debug] + steps: + - uses: actions/checkout@v2 + - name: Prepare and compile dependencies + run: python cue.py prepare + - name: Build main module (example app) + run: python cue.py build + - name: Run main module tests + run: python cue.py test + - name: Collect and show test results + run: python cue.py test-results + + build-windows: + name: ${{ matrix.cmp }} / ${{ matrix.configuration }} / ${{ matrix.os }} + runs-on: ${{ matrix.os }} + env: + CMP: ${{ matrix.cmp }} + BCFG: ${{ matrix.configuration }} + strategy: + fail-fast: false + matrix: + os: [windows-2019, windows-2016] + cmp: [gcc, vs2019, vs2017] + configuration: [default, static, debug, static-debug] + exclude: + - os: windows-2019 + cmp: vs2017 + - os: windows-2016 + cmp: vs2019 + steps: + - uses: actions/checkout@v2 + - name: Prepare and compile dependencies + run: python cue.py prepare + - name: Build main module (example app) + run: python cue.py build + - name: Run main module tests + run: python cue.py test + - name: Collect and show test results + run: python cue.py test-results + + build-rtems: + name: RTEMS${{ matrix.rtems }} / ${{ matrix.configuration }} / ${{ matrix.os }} + runs-on: ${{ matrix.os }} + env: + CMP: ${{ matrix.cmp }} + BCFG: ${{ matrix.configuration }} + RTEMS: ${{ matrix.rtems }} + APT: re2c g++-mingw-w64-i686 g++-mingw-w64-x86-64 qemu-system-x86 + strategy: + fail-fast: false + matrix: + os: [ubuntu-18.04] + cmp: [gcc] + configuration: [default, static, debug, static-debug] + rtems: ["4.9", "4.10"] + steps: + - uses: actions/checkout@v2 + - name: Prepare and compile dependencies + run: python cue.py prepare + - name: Build main module (example app) + run: python cue.py build + - name: Run main module tests + run: python cue.py test + - name: Collect and show test results + run: python cue.py test-results + + build-wine: + name: WINE${{ matrix.wine }} / ${{ matrix.configuration }} / ${{ matrix.os }} + runs-on: ${{ matrix.os }} + env: + CMP: ${{ matrix.cmp }} + BCFG: ${{ matrix.configuration }} + WINE: ${{ matrix.wine }} + APT: re2c g++-mingw-w64-i686 g++-mingw-w64-x86-64 + strategy: + fail-fast: false + matrix: + os: [ubuntu-18.04] + cmp: [gcc] + configuration: [default, static, debug, static-debug] + wine: [32, 64] + steps: + - uses: actions/checkout@v2 + - name: Prepare and compile dependencies + run: python cue.py prepare + - name: Build main module (example app) + run: python cue.py build + - name: Run main module tests + run: python cue.py test + - name: Collect and show test results + run: python cue.py test-results diff --git a/README.md b/README.md index 48a1db1..ea9a237 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ ![Version][badge.version] ![Travis status][badge.travis] ![AppVeyor status][badge.appveyor] +![GitHub Actions status][badge.gh-actions] # Continuous Integration for EPICS Modules @@ -58,12 +59,12 @@ levels as the example files. ### [Travis-CI](https://travis-ci.org/) - Five parallel runners on Linux/Windows (one runner on MacOS) - - Use different compilers (gcc, clang) - - Use different gcc versions - - Cross-compile for Windows 32bit and 64bit using MinGW and WINE - - Cross-compile for RTEMS 4.9 and 4.10 (Base >= 3.15) + - Ubuntu 12/14/16/18, MacOS 10.13, Windows Server v1809 + - Compile natively on Linux (different versions of gcc, clang) - Compile natively on MacOS (clang) - Compile natively on Windows (gcc/MinGW, Visual Studio 2017) + - Cross-compile for Windows 32bit and 64bit using MinGW and WINE + - Cross-compile for RTEMS 4.9 and 4.10 (Base >= 3.15) - Built dependencies are cached (for faster builds). See specific @@ -72,8 +73,8 @@ for more details. ### [AppVeyor](https://www.appveyor.com/) - One parallel runner (all builds are sequential) - - Use different compilers (Visual Studio, gcc/MinGW) - - Use different Visual Studio versions: \ + - Windows Server 2012/2016/2019 + - Compile using gcc/MinGW or different Visual Studio versions: \ 2008, 2010, 2012, 2013, 2015, 2017, 2019 - Compile for Windows 32bit and 64bit - No useful caching available. @@ -82,15 +83,30 @@ See specific **[ci-scripts on AppVeyor README](appveyor/README.md)** for more details. +### [GitHub Actions](https://github.com/) + - 20 parallel runners on Linux/Windows (5 runners on MacOS) + - Ubuntu 16/18/20, MacOS 10.15, Windows Server 2016/2019 + - Compile natively on Linux (gcc, clang) + - Compile natively on MacOS (clang) + - Compile natively on Windows (gcc/MinGW, Visual Studio 2017 & 2019) + - Cross-compile for Windows 32bit and 64bit using MinGW and WINE + - Cross-compile for RTEMS 4.9 and 4.10 (Base >= 3.15) + - Caching not supported by ci-scripts yet. + +See specific +**[ci-scripts on GitHub Actions README](gh-actions/README.md)** +for more details. + ## How to Use the CI-Scripts - 1. Get an account on a supported CI service provider platform. + 1. Get an account on a supported CI service provider platform (e.g. [Travis-CI](https://travis-ci.org/), - [AppVeyor](https://www.appveyor.com/), ...) + [AppVeyor](https://www.appveyor.com/), ...). + GitHub Actions does not require a separate account. (More details in the specific README of the subdirectory.) - 2. In your Support Module, add this ci-scripts repository + 2. In your module, add this ci-scripts repository as a Git Submodule (name suggestion: `.ci`). ```bash git submodule add https://github.com/epics-base/ci-scripts .ci @@ -405,6 +421,7 @@ in file LICENSE that is included with this distribution. [badge.version]: https://badge.fury.io/gh/epics-base%2Fci-scripts.svg [badge.travis]: https://travis-ci.org/epics-base/ci-scripts.svg?branch=master [badge.appveyor]: https://ci.appveyor.com/api/projects/status/8b578alg974axvux?svg=true +[badge.gh-actions]: https://github.com/epics-base/ci-scripts/workflows/ci-scripts%20build/test/badge.svg [reddit.bash]: https://www.reddit.com/r/bash/comments/393oqv/why_is_the_version_of_bash_included_in_os_x_so_old/ diff --git a/appveyor/README.md b/appveyor/README.md index e4005f3..1e0d40d 100644 --- a/appveyor/README.md +++ b/appveyor/README.md @@ -3,8 +3,8 @@ ## Features - One parallel runner (all builds are sequential) - - Use different compilers (Visual Studio, gcc/MinGW) - - Use different Visual Studio versions: \ + - Windows Server 2012/2016/2019 + - Compile using gcc/MinGW or different Visual Studio versions: \ 2008, 2010, 2012, 2013, 2015, 2017, 2019 - Compile for Windows 32bit and 64bit - No useful caching available. diff --git a/cue-test.py b/cue-test.py index 4f7124d..b431e81 100644 --- a/cue-test.py +++ b/cue-test.py @@ -31,6 +31,13 @@ if 'APPVEYOR' in os.environ: elif re.match(r'^macOS', os.environ['APPVEYOR_BUILD_WORKER_IMAGE']): ci_os = 'osx' +if 'GITHUB_ACTIONS' in os.environ: + ci_service = 'github-actions' + if os.environ['RUNNER_OS'] == 'macOS': + ci_os = 'osx' + else: + ci_os = os.environ['RUNNER_OS'].lower() + def find_in_file(regex, filename): file = open(filename, "r") @@ -324,8 +331,14 @@ class TestDefaultModuleURLs(unittest.TestCase): @unittest.skipIf(ci_os != 'windows', 'VCVars test only applies to windows') class TestVCVars(unittest.TestCase): def test_vcvars(self): - if ci_service == 'appveyor': + if ci_service == 'travis': + os.environ['TRAVIS_COMPILER'] = 'vs2017' + else: os.environ['CONFIGURATION'] = 'default' + if ci_service == 'github-actions' and os.environ['IMAGEOS'] == 'win16': + os.environ['CMP'] = 'vs2017' + else: + os.environ['CMP'] = 'vs2019' cue.detect_context() cue.with_vcvars('env') @@ -676,7 +689,7 @@ class TestSetupForBuild(unittest.TestCase): args = Namespace(paths=[]) cue.building_base = True if ci_os == 'windows': - sp.check_call(['choco', 'install', 'make']) + sp.check_call(['choco', 'install', 'make', 'strawberryperl', '-ry']) def setUp(self): if ci_service == 'appveyor': @@ -726,10 +739,10 @@ class TestSetupForBuild(unittest.TestCase): @unittest.skipIf(ci_os != 'windows', 'HostArchPlatform test only applies to windows') def test_HostArchPlatform(self): - if ci_service == 'travis': - platforms = ['x64'] - else: + if ci_service == 'appveyor': platforms = ['x86', 'x64'] + else: + platforms = ['x64'] for platform in platforms: for cc in ['vs2019', 'gcc']: cue.ci['platform'] = platform @@ -750,18 +763,17 @@ class TestSetupForBuild(unittest.TestCase): self.assertTrue(re.search('-mingw$', os.environ['EPICS_HOST_ARCH']), 'EPICS_HOST_ARCH (found {0}) is not -mingw for {1} / {2}' .format(os.environ['EPICS_HOST_ARCH'], cc, platform)) - pattern = {'x86': 'mingw32', 'x64': 'mingw64'} - self.assertTrue(re.search(pattern[platform], os.environ['PATH']), - 'Binary location for {0} not in PATH (found {1})' - .format(pattern[platform], os.environ['PATH'])) + if ci_service == 'appveyor': + pattern = {'x86': 'mingw32', 'x64': 'mingw64'} + self.assertTrue(re.search(pattern[platform], os.environ['PATH']), + 'Binary location for {0} not in PATH (found PATH = {1})' + .format(pattern[platform], os.environ['PATH'])) @unittest.skipIf(ci_os != 'windows', 'Strawberry perl test only applies to windows') - def test_StrawberryInPathVS2019(self): - if 'APPVEYOR' in os.environ: - os.environ['CMP'] = 'vs2019' + def test_StrawberryInPath(self): cue.setup_for_build(self.args) self.assertTrue(re.search('strawberry', os.environ['PATH'], flags=re.IGNORECASE), - 'Strawberry Perl installed but location not in PATH (found {0})' + 'Strawberry Perl location not in PATH (found PATH = {0})' .format(os.environ['PATH'])) def setBase314(self, yesno): @@ -827,5 +839,14 @@ if __name__ == "__main__": if sys.argv[1:] == ['env']: # testing with_vcvars [print(K, '=', V) for K, V in os.environ.items()] + elif ci_os == 'windows' and sys.argv[1:] == ['findvs']: + from fnmatch import fnmatch + print('Available Visual Studio versions') + for base in (r'C:\Program Files (x86)', r'C:\Program Files'): + for root, dirs, files in os.walk(base): + for fname in files: + if fnmatch(fname, 'vcvarsall.bat'): + print('Found', os.path.join(root, fname)) + sys.stdout.flush() else: unittest.main() diff --git a/cue.py b/cue.py index c7732f3..6a9b737 100644 --- a/cue.py +++ b/cue.py @@ -1,5 +1,5 @@ #!/usr/bin/env python -"""CI build script for Linux/MacOS/Windows on Travis/AppVeyor +"""CI build script for Linux/MacOS/Windows on Travis/AppVeyor/GitHub-Actions """ from __future__ import print_function @@ -22,11 +22,10 @@ def detect_context(): ci['os'] = os.environ['TRAVIS_OS_NAME'] ci['platform'] = 'x64' ci['compiler'] = os.environ['TRAVIS_COMPILER'] - if ci['os'] == 'windows': - ci['choco'] += ['strawberryperl'] - if re.match(r'^vs', ci['compiler']): - # Only Visual Studio 2017 available - ci['compiler'] = 'vs2017' + ci['choco'] += ['strawberryperl'] + if re.match(r'^vs', ci['compiler']): + # Only Visual Studio 2017 available + ci['compiler'] = 'vs2017' if 'BCFG' in os.environ: buildconfig = os.environ['BCFG'].lower() @@ -43,6 +42,24 @@ def detect_context(): ci['compiler'] = os.environ['CMP'] buildconfig = os.environ['CONFIGURATION'].lower() + if 'GITHUB_ACTIONS' in os.environ: + ci['service'] = 'github-actions' + if os.environ['RUNNER_OS'] == 'macOS': + ci['os'] = 'osx' + else: + ci['os'] = os.environ['RUNNER_OS'].lower() + ci['platform'] = 'x64' + if 'CMP' in os.environ: + ci['compiler'] = os.environ['CMP'] + ci['choco'] += ['strawberryperl'] + if 'BCFG' in os.environ: + buildconfig = os.environ['BCFG'].lower() + + if re.search('static', buildconfig): + ci['static'] = True + if re.search('debug', buildconfig): + ci['debug'] = True + if 'STATIC' in os.environ: print("{0}WARNING: Variable 'STATIC' not supported anymore; use 'BCFG' instead{1}" .format(ANSI_RED, ANSI_RESET)) @@ -52,11 +69,6 @@ def detect_context(): .format(ANSI_RED, buildconfig, ANSI_RESET)) sys.stdout.flush() - if re.search('static', buildconfig): - ci['static'] = True - if re.search('debug', buildconfig): - ci['debug'] = True - if ci['static']: ci['configuration'] = 'static' else: @@ -74,6 +86,9 @@ def detect_context(): if 'APT' in os.environ: ci['apt'].extend(os.environ['APT'].split()) + if 'BREW' in os.environ: + ci['homebrew'].extend(os.environ['BREW'].split()) + ci['test'] = True if 'TEST' in os.environ and os.environ['TEST'].lower() == 'no': ci['test'] = False @@ -130,6 +145,7 @@ def clear_lists(): ci['scriptsdir'] = '' ci['choco'] = ['make'] ci['apt'] = [] + ci['homebrew'] = [] clear_lists() @@ -153,11 +169,16 @@ ANSI_CLEAR = "\033[0K" # Travis log fold control # from https://github.com/travis-ci/travis-rubies/blob/build/build.sh +# GitHub Actions fold control +# from https://github.com/actions/toolkit/blob/master/docs/commands.md#group-and-ungroup-log-lines def fold_start(tag, title): if ci['service'] == 'travis': print('travis_fold:start:{0}{1}{2}{3}' .format(tag, ANSI_YELLOW, title, ANSI_RESET)) + elif ci['service'] == 'github-actions': + print('::group::{0}{1}{2}' + .format(ANSI_YELLOW, title, ANSI_RESET)) elif ci['service'] == 'appveyor': print('{0}===== \\/ \\/ \\/ ===== START: {1} ====={2}' .format(ANSI_YELLOW, title, ANSI_RESET)) @@ -168,6 +189,9 @@ def fold_end(tag, title): if ci['service'] == 'travis': print('\ntravis_fold:end:{0}\r' .format(tag), end='') + elif ci['service'] == 'github-actions': + print('::endgroup::' + .format(ANSI_YELLOW, title, ANSI_RESET)) elif ci['service'] == 'appveyor': print('{0}----- /\\ /\\ /\\ ----- END: {1} -----{2}' .format(ANSI_YELLOW, title, ANSI_RESET)) @@ -181,7 +205,7 @@ elif 'HOME' in os.environ: homedir = os.getenv('HOME') cachedir = os.path.join(homedir, '.cache') toolsdir = os.path.join(homedir, '.tools') -rtemsdir = os.path.join(homedir, '.rtems') +rtemsdir = r'/home/travis/.rtems' # Preliminary, until the next generation of toolchain if 'CACHEDIR' in os.environ: cachedir = os.environ['CACHEDIR'] @@ -189,8 +213,10 @@ if 'CACHEDIR' in os.environ: vcvars_table = { # https://en.wikipedia.org/wiki/Microsoft_Visual_Studio#History - 'vs2019': [r'C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvarsall.bat'], + 'vs2019': [r'C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvarsall.bat', + r'C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvarsall.bat'], 'vs2017': [r'C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvarsall.bat', + r'C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\VC\Auxiliary\Build\vcvarsall.bat', r'C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\VC\Auxiliary\Build\vcvarsall.bat'], 'vs2015': [r'C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat'], 'vs2013': [r'C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\vcvarsall.bat'], @@ -551,32 +577,34 @@ def setup_for_build(args): global is_base314, has_test_results, is_make3 dllpaths = [] + logger.debug('Setting up the build environment') + if ci['os'] == 'windows': - if ci['service'] == 'appveyor': - if ci['compiler'] == 'vs2019': - # put strawberry perl in the PATH - os.environ['PATH'] = os.pathsep.join([os.path.join(r'C:\Strawberry\perl\site\bin'), - os.path.join(r'C:\Strawberry\perl\bin'), - os.environ['PATH']]) - if ci['compiler'] == 'gcc': - if 'INCLUDE' not in os.environ: - os.environ['INCLUDE'] = '' - if ci['platform'] == 'x86': - os.environ['INCLUDE'] = os.pathsep.join( - [r'C:\mingw-w64\i686-6.3.0-posix-dwarf-rt_v5-rev1\mingw32\include', - os.environ['INCLUDE']]) - os.environ['PATH'] = os.pathsep.join([r'C:\mingw-w64\i686-6.3.0-posix-dwarf-rt_v5-rev1\mingw32\bin', - os.environ['PATH']]) - elif ci['platform'] == 'x64': - os.environ['INCLUDE'] = os.pathsep.join( - [r'C:\mingw-w64\x86_64-8.1.0-posix-seh-rt_v6-rev0\mingw64\include', - os.environ['INCLUDE']]) - os.environ['PATH'] = os.pathsep.join([r'C:\mingw-w64\x86_64-8.1.0-posix-seh-rt_v6-rev0\mingw64\bin', - os.environ['PATH']]) - if ci['service'] == 'travis': - os.environ['PATH'] = os.pathsep.join([r'C:\Strawberry\perl\site\bin', r'C:\Strawberry\perl\bin', + if os.path.exists(r'C:\Strawberry\perl\bin'): + # Put strawberry perl in front of the PATH (so that Git Perl is further behind) + logger.debug('Adding Strawberry Perl in front of the PATH') + os.environ['PATH'] = os.pathsep.join([r'C:\Strawberry\c\bin', + r'C:\Strawberry\perl\site\bin', + r'C:\Strawberry\perl\bin', os.environ['PATH']]) + if ci['service'] == 'appveyor' and ci['compiler'] == 'gcc': + logger.debug('Adding AppVeyor MSYS2/MinGW installation to PATH and INCLUDE') + if 'INCLUDE' not in os.environ: + os.environ['INCLUDE'] = '' + if ci['platform'] == 'x86': + os.environ['INCLUDE'] = os.pathsep.join( + [r'C:\mingw-w64\i686-6.3.0-posix-dwarf-rt_v5-rev1\mingw32\include', + os.environ['INCLUDE']]) + os.environ['PATH'] = os.pathsep.join([r'C:\mingw-w64\i686-6.3.0-posix-dwarf-rt_v5-rev1\mingw32\bin', + os.environ['PATH']]) + elif ci['platform'] == 'x64': + os.environ['INCLUDE'] = os.pathsep.join( + [r'C:\mingw-w64\x86_64-8.1.0-posix-seh-rt_v6-rev0\mingw64\include', + os.environ['INCLUDE']]) + os.environ['PATH'] = os.pathsep.join([r'C:\mingw-w64\x86_64-8.1.0-posix-seh-rt_v6-rev0\mingw64\bin', + os.environ['PATH']]) + # Find BASE location if not building_base: with open(os.path.join(cachedir, 'RELEASE.local'), 'r') as f: @@ -588,6 +616,8 @@ def setup_for_build(args): else: places['EPICS_BASE'] = '.' + logger.debug('Using EPICS Base at %s', places['EPICS_BASE']) + detect_epics_host_arch() if ci['os'] == 'windows': @@ -611,6 +641,7 @@ def setup_for_build(args): with open(cfg_base_version) as myfile: if 'BASE_3_14=YES' in myfile.read(): is_base314 = True + logger.debug('Check if EPICS Base is a 3.14 series: %s', is_base314) if not is_base314: rules_build = os.path.join(places['EPICS_BASE'], 'configure', 'RULES_BUILD') @@ -623,6 +654,7 @@ def setup_for_build(args): # Check make version if re.match(r'^GNU Make 3', sp.check_output(['make', '-v']).decode('ascii')): is_make3 = True + logger.debug('Check if make is a 3.x series: %s', is_make3) # apparently %CD% is handled automagically os.environ['TOP'] = os.getcwd() @@ -781,14 +813,17 @@ CROSS_COMPILER_TARGET_ARCHS += windows-x64-mingw''') RTEMS_VERSION={0} RTEMS_BASE={1}'''.format(os.environ['RTEMS'], rtemsdir)) - # Base 3.15 doesn't have -qemu target architecture - qemu_suffix = '' - if os.path.exists(os.path.join(places['EPICS_BASE'], 'configure', 'os', + # Patch Base 3.15 that doesn't have -qemu target architecture + if not os.path.exists(os.path.join(places['EPICS_BASE'], 'configure', 'os', 'CONFIG.Common.RTEMS-pc386-qemu')): - qemu_suffix = '-qemu' + print('Adding RTEMS-pc386-qemu target to Base in {0}'.format(places['EPICS_BASE'])) + sys.stdout.flush() + sp.check_call(['patch', '-p1', '-i', + os.path.join(ci['scriptsdir'], 'add-RTEMS-pc368-qemu-target.patch')], + cwd=places['EPICS_BASE']) with open(os.path.join(places['EPICS_BASE'], 'configure', 'CONFIG_SITE'), 'a') as f: f.write(''' -CROSS_COMPILER_TARGET_ARCHS += RTEMS-pc386{0}'''.format(qemu_suffix)) +CROSS_COMPILER_TARGET_ARCHS += RTEMS-pc386-qemu''') host_ccmplr_name = re.sub(r'^([a-zA-Z][^-]*(-[a-zA-Z][^-]*)*)+(-[0-9.]|)$', r'\1', ci['compiler']) host_cmplr_ver_suffix = re.sub(r'^([a-zA-Z][^-]*(-[a-zA-Z][^-]*)*)+(-[0-9.]|)$', r'\3', ci['compiler']) @@ -819,20 +854,24 @@ CMPLR_CLASS = clang''') CC = {0}{2} CCC = {1}{2}'''.format(host_ccmplr_name, host_cppcmplr_name, host_cmplr_ver_suffix)) - # Add additional flags to CONFIG_SITE - flags_text = '' + # Add additional settings to CONFIG_SITE + extra_config = '' if 'USR_CPPFLAGS' in os.environ: - flags_text += ''' + extra_config += ''' USR_CPPFLAGS += {0}'''.format(os.environ['USR_CPPFLAGS']) if 'USR_CFLAGS' in os.environ: - flags_text += ''' + extra_config += ''' USR_CFLAGS += {0}'''.format(os.environ['USR_CFLAGS']) if 'USR_CXXFLAGS' in os.environ: - flags_text += ''' + extra_config += ''' USR_CXXFLAGS += {0}'''.format(os.environ['USR_CXXFLAGS']) - if flags_text: + if ci['service'] == 'github-actions' and ci['os'] == 'windows': + extra_config += ''' +PERL = C:/Strawberry/perl/bin/perl -CSD''' + + if extra_config: with open(os.path.join(places['EPICS_BASE'], 'configure', 'CONFIG_SITE'), 'a') as f: - f.write(flags_text) + f.write(extra_config) fold_end('set.up.epics_build', 'Configuring EPICS build system') @@ -841,7 +880,7 @@ USR_CXXFLAGS += {0}'''.format(os.environ['USR_CXXFLAGS']) if ci['os'] == 'windows' and ci['choco']: fold_start('install.choco', 'Installing CHOCO packages') - sp.check_call(['choco', 'install'] + ci['choco']) + sp.check_call(['choco', 'install'] + ci['choco'] + ['-y', '--limitoutput', '--no-progress']) fold_end('install.choco', 'Installing CHOCO packages') if ci['os'] == 'linux' and ci['apt']: @@ -849,6 +888,11 @@ USR_CXXFLAGS += {0}'''.format(os.environ['USR_CXXFLAGS']) sp.check_call(['sudo', 'apt-get', '-y', 'install'] + ci['apt']) fold_end('install.apt', 'Installing APT packages') + if ci['os'] == 'osx' and ci['homebrew']: + fold_start('install.homebrew', 'Installing Homebrew packages') + sp.check_call(['brew', 'install'] + ci['homebrew']) + fold_end('install.homebrew', 'Installing Homebrew packages') + if ci['os'] == 'linux' and 'RTEMS' in os.environ: tar_name = 'i386-rtems{0}-trusty-20171203-{0}.tar.bz2'.format(os.environ['RTEMS']) print('Downloading RTEMS {0} cross compiler: {1}' @@ -858,7 +902,10 @@ USR_CXXFLAGS += {0}'''.format(os.environ['USR_CXXFLAGS']) 'https://github.com/mdavidsaver/rsb/releases/download/20171203-{0}/{1}' .format(os.environ['RTEMS'], tar_name)], cwd=toolsdir) - sp.check_call(['tar', '-C', '/', '-xmj', '-f', os.path.join(toolsdir, tar_name)]) + sudo_prefix = [] + if ci['service'] == 'github-actions': + sudo_prefix = ['sudo'] + sp.check_call(sudo_prefix + ['tar', '-C', '/', '-xmj', '-f', os.path.join(toolsdir, tar_name)]) os.remove(os.path.join(toolsdir, tar_name)) setup_for_build(args) diff --git a/github-actions/README.md b/github-actions/README.md new file mode 100644 index 0000000..77ab32d --- /dev/null +++ b/github-actions/README.md @@ -0,0 +1,57 @@ +# GitHub Actions Scripts for EPICS Modules + +## Features + + - 20 parallel runners on Linux/Windows (5 runners on MacOS) + - Ubuntu 16/18/20, MacOS 10.15, Windows Server 2016/2019 + - Compile natively on Linux (gcc, clang) + - Compile natively on MacOS (clang) + - Compile natively on Windows (gcc/MinGW, Visual Studio 2017 & 2019) + - Cross-compile for Windows 32bit and 64bit using MinGW and WINE + - Cross-compile for RTEMS 4.9 and 4.10 (Base >= 3.15) + - Caching not supported yet. + +## How to Use these Scripts + + 1. Add the ci-scripts respository as a Git Submodule + (see [README](../README.md) one level above). + + 2. Add settings files defining which dependencies in which versions + you want to build against + (see [README](../README.md) one level above). + + 3. Create a GitHub Actions configuration by copying one of the workflow + examples into the directory `.github/workflows` of your module. + ```bash + $ mkdir -p .github/workflows + $ cp .ci/github-actions/ci-scripts-build.yml.example-full .github/workflows/ci-scripts-build.yml + ``` + + 4. Edit the workflow configuration to include the build jobs you want + GitHub Actions to run. + + Build jobs are specified in the `jobs: : strategy:` + declaration. The `matrix:` element specifies the axes as configuration + parameters with their lists of values, + `env:` (on the build level) controls the setting of environment variables + (which can be matrix parameters). + The `runs-on:` setting specifies the image (operating system) of the + runner. + The `name:` is what shows up in the web interface for the workflow, + builds and jobs, and the elements under `steps:` describe the actions + executed for each job of the matrix. + + Please check the comments in the examples for more hints, and the + [GitHub Actions documentation](https://help.github.com/en/actions) + for a lot more options and details. + + 5. Push your changes and click on the `Actions` tab of your GitHub repository + page to see your build results. + +## Caches + +GitHub Actions provides caching of dependencies. + +However, since their cache restore and create algorithm is fundamentally +different from those used by Travis and AppVeyor, this will require some +more changes in ci-scripts to work. Be patient. diff --git a/github-actions/ci-scripts-build.yml.example-full b/github-actions/ci-scripts-build.yml.example-full new file mode 100644 index 0000000..412a6b6 --- /dev/null +++ b/github-actions/ci-scripts-build.yml.example-full @@ -0,0 +1,155 @@ +# .github/workflows/ci-scripts-build.yml for use with EPICS Base ci-scripts +# (see: https://github.com/epics-base/ci-scripts) + +# This is YAML - indentation levels are crucial + +# Set the 'name:' properties to values that work for you (MYMODULE) + +name: MYMODULE ci-scripts build + +# Trigger on pushes and PRs to any branch +on: [push, pull_request] + +env: + SETUP_PATH: .ci-local:.ci + SET: test01 + CMP: gcc + # For the sequencer on Linux/Windows/MacOS + APT: re2c + CHOCO: re2c + BREW: re2c + +jobs: + build-linux: + name: ${{ matrix.cmp }} / ${{ matrix.configuration }} / ${{ matrix.os }} + runs-on: ${{ matrix.os }} + # Set environment variables from matrix parameters + env: + CMP: ${{ matrix.cmp }} + BCFG: ${{ matrix.configuration }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-20.04, ubuntu-18.04, ubuntu-16.04] + cmp: [gcc, clang] + configuration: [default, static, debug, static-debug] + steps: + - uses: actions/checkout@v2 + - name: Prepare and compile dependencies + run: python cue.py prepare + - name: Build main module + run: python cue.py build + - name: Run main module tests + run: python cue.py test + - name: Collect and show test results + run: python cue.py test-results + + build-macos: + name: ${{ matrix.cmp }} / ${{ matrix.configuration }} / ${{ matrix.os }} + runs-on: ${{ matrix.os }} + # Set environment variables from matrix parameters + env: + CMP: ${{ matrix.cmp }} + BCFG: ${{ matrix.configuration }} + strategy: + fail-fast: false + matrix: + os: [macos-10.15] + cmp: [clang] + # No static builds on MacOS + configuration: [default, debug] + steps: + - uses: actions/checkout@v2 + - name: Prepare and compile dependencies + run: python cue.py prepare + - name: Build main module + run: python cue.py build + - name: Run main module tests + run: python cue.py test + - name: Collect and show test results + run: python cue.py test-results + + build-windows: + name: ${{ matrix.cmp }} / ${{ matrix.configuration }} / ${{ matrix.os }} + runs-on: ${{ matrix.os }} + env: + CMP: ${{ matrix.cmp }} + BCFG: ${{ matrix.configuration }} + strategy: + fail-fast: false + matrix: + os: [windows-2019, windows-2016] + cmp: [gcc, vs2019, vs2017] + configuration: [default, static, debug, static-debug] + # Available: vs2017/windows-2016 and vs2019/windows-2019 + exclude: + - os: windows-2019 + cmp: vs2017 + - os: windows-2016 + cmp: vs2019 + steps: + - uses: actions/checkout@v2 + - name: Prepare and compile dependencies + run: python cue.py prepare + - name: Build main module + run: python cue.py build + - name: Run main module tests + run: python cue.py test + - name: Collect and show test results + run: python cue.py test-results + + # Same setup and toolchain as on Travis. + # Needs Base >= 3.15 to compile, EPICS 7 to also run the tests on qemu + build-rtems: + name: RTEMS${{ matrix.rtems }} / ${{ matrix.configuration }} / ${{ matrix.os }} + runs-on: ${{ matrix.os }} + env: + CMP: ${{ matrix.cmp }} + BCFG: ${{ matrix.configuration }} + RTEMS: ${{ matrix.rtems }} + APT: re2c g++-mingw-w64-i686 g++-mingw-w64-x86-64 qemu-system-x86 + strategy: + fail-fast: false + matrix: + os: [ubuntu-18.04] + cmp: [gcc] + configuration: [default, static, debug, static-debug] + rtems: ["4.9", "4.10"] + steps: + - uses: actions/checkout@v2 + - name: Prepare and compile dependencies + run: python cue.py prepare + - name: Build main module + run: python cue.py build + - name: Run main module tests + run: python cue.py test + - name: Collect and show test results + run: python cue.py test-results + + # The WINE cross builds are of somewhat limited use, + # as there are native gcc/MinGW builds available on GitHub Actions + build-wine: + name: WINE${{ matrix.wine }} / ${{ matrix.configuration }} / ${{ matrix.os }} + runs-on: ${{ matrix.os }} + env: + CMP: ${{ matrix.cmp }} + BCFG: ${{ matrix.configuration }} + WINE: ${{ matrix.wine }} + APT: re2c g++-mingw-w64-i686 g++-mingw-w64-x86-64 + strategy: + fail-fast: false + matrix: + os: [ubuntu-18.04] + cmp: [gcc] + configuration: [default, static, debug, static-debug] + wine: [32, 64] + steps: + - uses: actions/checkout@v2 + - name: Prepare and compile dependencies + run: python cue.py prepare + - name: Build main module + run: python cue.py build + - name: Run main module tests + run: python cue.py test + - name: Collect and show test results + run: python cue.py test-results diff --git a/github-actions/ci-scripts-build.yml.example-mini b/github-actions/ci-scripts-build.yml.example-mini new file mode 100644 index 0000000..6c20072 --- /dev/null +++ b/github-actions/ci-scripts-build.yml.example-mini @@ -0,0 +1,43 @@ +# .github/workflows/ci-scripts-build.yml for use with EPICS Base ci-scripts +# (see: https://github.com/epics-base/ci-scripts) + +# This is YAML - indentation levels are crucial + +# Set the 'name:' properties to values that work for you + +name: MYMODULE ci-scripts build + +# Trigger on pushes and PRs to any branch +on: [push, pull_request] + +env: + SETUP_PATH: .ci-local:.ci + SET: test01 + CMP: gcc + +jobs: + build-linux: + name: ${{ matrix.base }} / ${{ matrix.cmp }} / ${{ matrix.configuration }} / ${{ matrix.os }} + runs-on: ${{ matrix.os }} + # Set environment variables from matrix parameters + env: + CMP: ${{ matrix.cmp }} + BCFG: ${{ matrix.configuration }} + BASE: ${{ matrix.base }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-20.04, ubuntu-18.04] + cmp: [gcc] + configuration: [default, static] + base: ["7.0", "3.15"] + steps: + - uses: actions/checkout@v2 + - name: Prepare and compile dependencies + run: python cue.py prepare + - name: Build main module + run: python cue.py build + - name: Run main module tests + run: python cue.py test + - name: Collect and show test results + run: python cue.py test-results