diff --git a/CHANGELOG.md b/CHANGELOG.md index 2a4ea0157..f079b4064 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -44,10 +44,10 @@ #### RefC -* Adds support for `CFLAGS`, `CPPFLAGS`, and `LDFLAGS` to facilitate building on - systems with non-standard installation locations of libraries (e.g. GMP). - Versions of the flags with the `IDRIS2_` prefix can also be used and take - precedence. +* Adds support for `CFLAGS`, `CPPFLAGS`, `LDFLAGS` and `LDLIBS` to facilitate + building on systems with non-standard installation locations of libraries + (e.g. GMP). Versions of the flags with the `IDRIS2_` prefix can also be used + and take precedence. #### Chez diff --git a/CONTRIBUTORS b/CONTRIBUTORS index 0d8237250..c6e15d783 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -2,6 +2,7 @@ Thanks to the following for their help and contributions to Idris 2: Aaron Lebahn Abdelhakim Qbaich +Adam Brouwers-Harries Alain Zscheile Aleksei Volkov Alex Gryzlov diff --git a/src/Compiler/RefC/CC.idr b/src/Compiler/RefC/CC.idr index f927de422..ab3b3c2ae 100644 --- a/src/Compiler/RefC/CC.idr +++ b/src/Compiler/RefC/CC.idr @@ -7,6 +7,8 @@ import Core.Directory import System +import Data.String + %default total findCC : IO String @@ -41,6 +43,14 @@ findLDFLAGS | Just ldflags => pure ldflags pure "" +findLDLIBS : IO String +findLDLIBS + = do Nothing <- getEnv "IDRIS2_LDLIBS" + | Just ldlibs => pure ldlibs + Nothing <- getEnv "LDLIBS" + | Just ldlibs => pure ldlibs + pure "" + clibdirs : List String -> List String clibdirs ds = map (\d => "-L" ++ d) ds @@ -68,6 +78,8 @@ compileCObjectFile {asLibrary} sourceFile objectFile = "-o", objectFile, "-I" ++ refcDir, "-I" ++ cDir] + ++ (words cppFlags) + ++ (words cFlags) log "compiler.refc.cc" 10 runccobj 0 <- coreLift $ system runccobj @@ -85,6 +97,7 @@ compileCFile {asShared} objectFile outFile = do cc <- coreLift findCC cFlags <- coreLift findCFLAGS ldFlags <- coreLift findLDFLAGS + ldLibs <- coreLift findLDLIBS dirs <- getDirs refcDir <- findDataFile "refc" @@ -100,6 +113,9 @@ compileCFile {asShared} objectFile outFile = "-L" ++ refcDir ] ++ clibdirs (lib_dirs dirs) ++ [ "-lgmp", "-lm"] + ++ (words cFlags) + ++ (words ldFlags) + ++ (words ldLibs) log "compiler.refc.cc" 10 runcc 0 <- coreLift $ system runcc diff --git a/src/Idris/Env.idr b/src/Idris/Env.idr index f63f08173..057804ec2 100644 --- a/src/Idris/Env.idr +++ b/src/Idris/Env.idr @@ -38,10 +38,12 @@ envs = [ MkEnvDesc "IDRIS2_CFLAGS" "RefC backend: C compiler flags.", MkEnvDesc "IDRIS2_CPPFLAGS" "RefC backend: C preprocessor flags.", MkEnvDesc "IDRIS2_LDFLAGS" "RefC backend: C linker flags.", + MkEnvDesc "IDRIS2_LDLIBS" "RefC backend: C linker library names or flags.", MkEnvDesc "CC" "RefC backend: C compiler executable (IDRIS2_CC takes precedence).", MkEnvDesc "CFLAGS" "RefC backend: C compiler flags (IDRIS2_CFLAGS takes precedence).", MkEnvDesc "CPPFLAGS" "RefC backend: C preprocessor flags (IDRIS2_CPPFLAGS takes precedence).", MkEnvDesc "LDFLAGS" "RefC backend: C linker flags (IDRIS2_LDFLAGS takes precedence).", + MkEnvDesc "LDLIBS" "RefC backend: C linker library names or flags (IDRIS2_LDLIBS takes precedence).", MkEnvDesc "NODE" "NodeJS backend: NodeJS executable.", MkEnvDesc "PATH" "PATH variable is used to search for executables in certain codegens.", MkEnvDesc "NO_COLOR" "Instruct Idris not to print color to stdout. Passing the --color/--colour option will supersede this env var."] diff --git a/tests/refc/ccompilerArgs/Main.idr b/tests/refc/ccompilerArgs/Main.idr new file mode 100644 index 000000000..fc3eff430 --- /dev/null +++ b/tests/refc/ccompilerArgs/Main.idr @@ -0,0 +1,15 @@ +import System.FFI + +libexternal : String -> String +libexternal fn = "C:" ++ fn ++ ",libexternalc,externalc.h" + +%foreign (libexternal "add") +add : Int -> Int -> Int + +%foreign (libexternal "fastfibsum") +fastfibsum : Int -> Int + +main : IO () +main = do + printLn $ show (add 50 23) + printLn $ show ([fastfibsum x | x <- [0..10]]) \ No newline at end of file diff --git a/tests/refc/ccompilerArgs/expected b/tests/refc/ccompilerArgs/expected new file mode 100644 index 000000000..f3e94419e --- /dev/null +++ b/tests/refc/ccompilerArgs/expected @@ -0,0 +1,2 @@ +"73" +"[0, 1, 2, 4, 7, 12, 20, 33, 54, 88, 143]" diff --git a/tests/refc/ccompilerArgs/library/Makefile b/tests/refc/ccompilerArgs/library/Makefile new file mode 100644 index 000000000..279a36d68 --- /dev/null +++ b/tests/refc/ccompilerArgs/library/Makefile @@ -0,0 +1,10 @@ +all: libexternalc.so + +externalc.o: externalc.c externalc.h + cc -c -fPIC $< -o $@ + +libexternalc.so: externalc.o + cc $< -shared -o $@ + +clean: + rm -f externalc.o externalc.so \ No newline at end of file diff --git a/tests/refc/ccompilerArgs/library/externalc.c b/tests/refc/ccompilerArgs/library/externalc.c new file mode 100644 index 000000000..bedc98eec --- /dev/null +++ b/tests/refc/ccompilerArgs/library/externalc.c @@ -0,0 +1,20 @@ +#include "externalc.h" + +int add(int x, int y) { + return x+y; +} + +int fastfibsum(int x) { + int acc = 0; + int p = 0; + int c = 1; + int tmp; + for (;0 <=-- x;) + { + acc += c; + tmp = c; + c = c + p; + p = tmp; + } + return acc; +} \ No newline at end of file diff --git a/tests/refc/ccompilerArgs/library/externalc.h b/tests/refc/ccompilerArgs/library/externalc.h new file mode 100644 index 000000000..4d136bdde --- /dev/null +++ b/tests/refc/ccompilerArgs/library/externalc.h @@ -0,0 +1,3 @@ +int add(int x, int y); + +int fastfibsum(int x); \ No newline at end of file diff --git a/tests/refc/ccompilerArgs/run b/tests/refc/ccompilerArgs/run new file mode 100755 index 000000000..7f462cc6a --- /dev/null +++ b/tests/refc/ccompilerArgs/run @@ -0,0 +1,30 @@ +. ../../testutils.sh + +# This test checks to make sure that Idris2's command line handling for the RefC backend is correct. +# It checks that: +# 1) Idris2 correctly finds `CFLAGS` and `LDFLAGS` in the environment, +# 2) The values in `CFLAGS` and `LDFLAGS` are separated correctly to be passed to the compiler +# +# (1) is achieved by compiling a c library (`externalc`) in a separate folder, and then explicitly +# pointing the compiler to the header files (with `-I./library/`) and shared library (with `-L./library`) +# and requesting that libexternalc.{so,dylib,dll} is linked (with `-lexternalc`). We additionally point the +# dynamic library loader to the correct location with `DYLD_LIBRARY_PATH`. +# +# (2) is achieved by passing multiple options, separated by spaces, in each of `CFLAGS` and `LDFLAGS`. +# These options are `-O3` for the c flags, and `-Wl,-pie` for the linker flags. They do not change the +# semantics of the resulting code (`-O3` simply optimises it more, and `-Wl,-pie` emits position +# independent code in the final executable), but do check that we correctly split up the environment +# variables when we pass them to the C compiler. + +cd ./library/ + make > /dev/null +cd .. + +export CFLAGS="-I./library/ -O3" +export LDFLAGS="-L./library/ -Wl,-pie" +export LDLIBS="-lexternalc" +export DYLD_LIBRARY_PATH="./library/" + +idris2 --cg refc -o cffi Main.idr > /dev/null + +./build/exec/cffi \ No newline at end of file