Skip to content

Commit c0ced6a

Browse files
authored
feat: Install C/C++ with Fortran, set FC/CC/CXX and FPM/CMake vars, and install CMake/Ninja by default (#63)
* feat: install cmake and ninja by default * feat: add support for C/C++ compilers and configure fpm/CMake environments * docs: update README
1 parent c315f47 commit c0ced6a

File tree

17 files changed

+1826
-409
lines changed

17 files changed

+1826
-409
lines changed

.github/workflows/CI-CD.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -100,11 +100,11 @@ jobs:
100100
compiler: [gfortran, ifx, lfortran, flang-new, nvfortran]
101101
include:
102102
- os: ubuntu-latest
103-
extra-packages: "cmake, ninja"
103+
extra-packages: ""
104104
- os: windows-latest
105-
extra-packages: "cmake, ninja"
105+
extra-packages: ""
106106
- os: macos-latest
107-
extra-packages: "cmake, ninja"
107+
extra-packages: ""
108108
exclude:
109109
- os: macos-latest
110110
compiler: flang-new

README.md

Lines changed: 42 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,42 @@
22

33
A GitHub Action that sets up a Fortran development environment using Conda. Inspired by [Conda + Fortran](https://degenerateconic.com/conda-plus-fortran.html).
44

5+
## Supported Compiler Configurations
6+
7+
The selected Fortran compiler is installed along with the corresponding C and C++ compilers, as well as `fpm`, `cmake` and `ninja`. Additional packages can be installed using the `extra-packages` input.
8+
9+
### Ubuntu
10+
11+
| Fortran Compiler | C Compiler | C++ Compiler |
12+
| ---------------- | ---------- | ------------ |
13+
| gfortran | gcc | g++ |
14+
| ifx | icx | icx |
15+
| lfortran | gcc | g++ |
16+
| flang, flang-new | clang | clang++ |
17+
| nvfortran | nvc | nvc++ |
18+
19+
### macOS
20+
21+
| Fortran Compiler | C Compiler | C++ Compiler |
22+
| ---------------- | ---------- | ------------ |
23+
| gfortran | gcc | g++ |
24+
| lfortran | clang | clang++ |
25+
26+
### Windows
27+
28+
| Fortran Compiler | C Compiler | C++ Compiler |
29+
| ---------------- | ---------- | ------------ |
30+
| gfortran | gcc | g++ |
31+
| ifx | icx | icx |
32+
| lfortran | clang-cl | clang-cl |
33+
| flang, flang-new | clang-cl | clang-cl |
34+
35+
**The following environment variables are automatically set:**
36+
37+
* `FC`, `CC`, `CXX`
38+
* `FPM_FC`, `FPM_CC`, `FPM_CXX`
39+
* `CMAKE_Fortran_COMPILER`, `CMAKE_C_COMPILER`, `CMAKE_CXX_COMPILER`
40+
541

642
## 📋 Workflow Example
743

@@ -149,11 +185,11 @@ jobs:
149185
compiler: [gfortran, ifx, lfortran, flang-new, nvfortran]
150186
include:
151187
- os: ubuntu-latest
152-
extra-packages: "cmake, ninja"
188+
extra-packages: ""
153189
- os: windows-latest
154-
extra-packages: "cmake, ninja"
190+
extra-packages: ""
155191
- os: macos-latest
156-
extra-packages: "cmake, ninja"
192+
extra-packages: ""
157193
exclude:
158194
- os: macos-latest
159195
compiler: flang-new
@@ -282,15 +318,15 @@ matrix:
282318
- os: ubuntu-latest
283319
compiler: gfortran
284320
compiler-version: 15.1.0
285-
extra-packages: "cmake, ninja"
321+
extra-packages: ""
286322
- os: macos-latest
287323
compiler: gfortran
288324
compiler-version: 15.1.0
289-
extra-packages: "cmake, ninja"
325+
extra-packages: ""
290326
- os: windows-latest
291327
compiler: gfortran
292328
compiler-version: 15.1.0
293-
extra-packages: "cmake, ninja"
329+
extra-packages: ""
294330
```
295331
Then, reference `compiler-version` in the setup step:
296332

extra.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { exec as _exec } from '@actions/exec';
22
import { startGroup, endGroup, info } from '@actions/core';
33

44
export async function installExtras(env = 'fortran', extras = []) {
5-
const pkgs = ['fpm', ...extras.map(p => p.trim()).filter(Boolean)];
5+
const pkgs = ['fpm', 'cmake', 'ninja', ...extras.map(p => p.trim()).filter(Boolean)];
66
if (!pkgs.length) return;
77

88
startGroup(`Installing extra packages: ${pkgs.join(', ')}`);
@@ -14,6 +14,9 @@ export async function installExtras(env = 'fortran', extras = []) {
1414
'-c',
1515
'conda-forge',
1616
...pkgs,
17+
'--update-all',
18+
'--all',
19+
'--force-reinstall'
1720
]);
1821
endGroup();
1922

platform/lin/flang-new.js

Lines changed: 115 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,65 +1,143 @@
1-
import {
2-
startGroup,
3-
endGroup,
4-
addPath,
5-
exportVariable,
6-
info,
7-
} from '@actions/core';
1+
import { startGroup, endGroup, addPath, info } from '@actions/core';
82
import { exec as _exec } from '@actions/exec';
93
import { sep, join } from 'path';
10-
import { appendFileSync } from 'fs';
4+
import { existsSync, appendFileSync } from 'fs';
115
import { EOL } from 'os';
6+
import { env, platform } from 'process';
127

8+
// Export a key=value pair to GITHUB_ENV and process.env
9+
function exportEnv(key, value) {
10+
const envFile = process.env.GITHUB_ENV;
11+
if (!envFile) throw new Error('GITHUB_ENV not defined');
12+
appendFileSync(envFile, `${key}=${value}${EOL}`);
13+
env[key] = value;
14+
}
15+
16+
// Resolve the absolute path of a named conda environment
1317
async function getCondaPrefix(envName) {
1418
let raw = '';
1519
await _exec('conda', ['env', 'list', '--json'], {
1620
silent: true,
17-
listeners: { stdout: d => (raw += d.toString()) },
21+
listeners: { stdout: (d) => (raw += d.toString()) }
1822
});
23+
1924
const { envs } = JSON.parse(raw);
2025
for (const p of envs) {
2126
if (p.endsWith(sep + envName) || p.endsWith('/' + envName)) return p;
2227
}
28+
2329
throw new Error(`Unable to locate Conda environment "${envName}".`);
2430
}
2531

26-
async function setUlimits() {
27-
if (process.platform !== 'linux') return;
28-
29-
startGroup('Set unlimited ulimits (Linux, persistent)');
30-
const ulimitCmd = 'ulimit -c unlimited -d unlimited -f unlimited -m unlimited -s unlimited -t unlimited -v unlimited -x unlimited';
31-
32-
// Inject into bash shell for all future steps
33-
appendFileSync(process.env.GITHUB_ENV, `BASH_ENV=${process.env.RUNNER_TEMP}/ulimit.sh${EOL}`);
34-
appendFileSync(`${process.env.RUNNER_TEMP}/ulimit.sh`, `${ulimitCmd}${EOL}`);
35-
32+
// Optional: Set unlimited ulimits for Linux
33+
function setLinuxUlimits() {
34+
startGroup('Setting unlimited ulimits (Linux)');
35+
const ulimitCmd =
36+
'ulimit -c unlimited -d unlimited -f unlimited -m unlimited -s unlimited -t unlimited -v unlimited -x unlimited';
37+
const script = `${process.env.RUNNER_TEMP}/ulimit.sh`;
38+
appendFileSync(script, `${ulimitCmd}${EOL}`);
39+
appendFileSync(process.env.GITHUB_ENV, `BASH_ENV=${script}${EOL}`);
40+
info('ulimit settings exported to BASH_ENV');
3641
endGroup();
3742
}
3843

44+
// Main setup function
3945
export async function setup(version = '') {
40-
const packageName = version ? `flang=${version}` : 'flang';
41-
42-
startGroup('Conda install');
43-
await _exec('conda', [
44-
'install',
45-
'--yes',
46-
'--name',
47-
'fortran',
48-
packageName,
49-
'-c',
50-
'conda-forge',
51-
]);
46+
if (platform !== 'linux') {
47+
throw new Error('This setup script is only supported on Linux.');
48+
}
49+
50+
// Define the set of Conda packages to install
51+
const Pkg = version ? `flang=${version}` : 'flang';
52+
const packages = [Pkg, 'llvm', 'clang-tools', 'llvm-openmp', 'lld'];
53+
54+
startGroup('Installing Conda packages');
55+
try {
56+
await _exec('conda', [
57+
'install',
58+
'--yes',
59+
'--name',
60+
'fortran',
61+
...packages,
62+
'-c',
63+
'conda-forge',
64+
'--update-all',
65+
'--all',
66+
'--force-reinstall'
67+
]);
68+
info('Conda packages installed');
69+
} catch (err) {
70+
throw new Error(`Conda install failed: ${err.message}`);
71+
}
5272
endGroup();
5373

74+
// Add Conda bin paths to PATH so tools are usable
5475
const prefix = await getCondaPrefix('fortran');
76+
const binPath = join(prefix, 'bin');
77+
const libPath = join(prefix, 'lib');
5578

56-
startGroup('Environment setup');
57-
addPath(join(prefix, 'bin'));
79+
startGroup('Setting up environment paths');
80+
const paths = [binPath];
81+
for (const p of paths) {
82+
if (existsSync(p)) {
83+
addPath(p);
84+
info(`Added to PATH: ${p}`);
85+
}
86+
}
87+
endGroup();
88+
89+
// Set LD_LIBRARY_PATH
90+
const ldPath = [libPath, env.LD_LIBRARY_PATH || ''].filter(Boolean).join(':');
91+
exportEnv('LD_LIBRARY_PATH', ldPath);
92+
info(`Set LD_LIBRARY_PATH → ${ldPath}`);
93+
94+
// Verify that the compilers are installed and working
95+
startGroup('Verifying compiler versions');
96+
await _exec('which', ['flang']);
97+
await _exec('flang', ['--version']);
98+
await _exec('which', ['clang']);
99+
await _exec('clang', ['--version']);
100+
await _exec('which', ['clang++']);
101+
await _exec('clang++', ['--version']);
102+
endGroup();
103+
104+
// Export compiler-related environment variables
105+
startGroup('Exporting compiler environment variables');
106+
const envVars = {
107+
FC: 'flang',
108+
CC: 'clang',
109+
CXX: 'clang++',
110+
FPM_FC: 'flang',
111+
FPM_CC: 'clang',
112+
FPM_CXX: 'clang++',
113+
CMAKE_Fortran_COMPILER: 'flang',
114+
CMAKE_C_COMPILER: 'clang',
115+
CMAKE_CXX_COMPILER: 'clang++',
116+
LD_LIBRARY_PATH: ldPath
117+
};
58118

59-
const newLd = `${join(prefix, 'lib')}:${process.env.LD_LIBRARY_PATH || ''}`;
60-
exportVariable('LD_LIBRARY_PATH', newLd);
61-
info(`LD_LIBRARY_PATH → ${newLd}`);
119+
for (const [key, value] of Object.entries(envVars)) {
120+
exportEnv(key, value);
121+
info(`Exported: ${key}=${value}`);
122+
}
123+
endGroup();
124+
125+
setLinuxUlimits();
126+
127+
// Export all environment variables to process.env and GITHUB_ENV
128+
startGroup('Exporting all environment variables to process.env and GITHUB_ENV');
129+
for (const [key, value] of Object.entries(env)) {
130+
if (typeof value === 'string') {
131+
try {
132+
process.env[key] = value;
133+
appendFileSync(process.env.GITHUB_ENV, `${key}=${value}${EOL}`);
134+
info(`Exported: ${key}`);
135+
} catch (err) {
136+
info(`⚠️ Failed to export: ${key} (${err.message})`);
137+
}
138+
}
139+
}
62140
endGroup();
63141

64-
await setUlimits();
142+
info('✅ compiler setup complete');
65143
}

0 commit comments

Comments
 (0)