diff --git a/CHANGELOG.md b/CHANGELOG.md index 427cb241bf..65ac3b8440 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 0d8237250e..c6e15d783f 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/docs/source/reference/envvars.rst b/docs/source/reference/envvars.rst index 54b45af22c..5d80c6d8b4 100644 --- a/docs/source/reference/envvars.rst +++ b/docs/source/reference/envvars.rst @@ -43,10 +43,12 @@ Both * ``IDRIS2_CFLAGS`` - RefC backend: C compiler flags. * ``IDRIS2_CPPFLAGS`` - RefC backend: C preprocessor flags. * ``IDRIS2_LDFLAGS`` - RefC backend: C linker flags. +* ``IDRIS2_LDLIBS`` - RefC backend: C linker library names or flags. * ``CC`` - RefC backend: C compiler executable (IDRIS2_CC takes precedence). * ``CFLAGS`` - RefC backend: C compiler flags (IDRIS2_CFLAGS takes precedence). * ``CPPFLAGS`` - RefC backend: C preprocessor flags (IDRIS2_CPPFLAGS takes precedence). * ``LDFLAGS`` - RefC backend: C linker flags (IDRIS2_LDFLAGS takes precedence). +* ``LDLIBS`` - RefC backend: C linker library names or flags. * ``NODE`` - NodeJS backend: ``node`` executable. * ``PATH`` - PATH variable is used to search for executables in certain codegens. diff --git a/src/Compiler/RefC/CC.idr b/src/Compiler/RefC/CC.idr index f927de4225..ec425e0eba 100644 --- a/src/Compiler/RefC/CC.idr +++ b/src/Compiler/RefC/CC.idr @@ -6,41 +6,52 @@ import Core.Options import Core.Directory import System +import Idris.Env + +import Data.String %default total findCC : IO String findCC - = do Nothing <- getEnv "IDRIS2_CC" + = do Nothing <- idrisGetEnv "IDRIS2_CC" | Just cc => pure cc - Nothing <- getEnv "CC" + Nothing <- idrisGetEnv "CC" | Just cc => pure cc pure "cc" findCFLAGS : IO String findCFLAGS - = do Nothing <- getEnv "IDRIS2_CFLAGS" + = do Nothing <- idrisGetEnv "IDRIS2_CFLAGS" | Just cflags => pure cflags - Nothing <- getEnv "CFLAGS" + Nothing <- idrisGetEnv "CFLAGS" | Just cflags => pure cflags pure "" findCPPFLAGS : IO String findCPPFLAGS - = do Nothing <- getEnv "IDRIS2_CPPFLAGS" + = do Nothing <- idrisGetEnv "IDRIS2_CPPFLAGS" | Just cppflags => pure cppflags - Nothing <- getEnv "CPPFLAGS" + Nothing <- idrisGetEnv "CPPFLAGS" | Just cppflags => pure cppflags pure "" findLDFLAGS : IO String findLDFLAGS - = do Nothing <- getEnv "IDRIS2_LDFLAGS" + = do Nothing <- idrisGetEnv "IDRIS2_LDFLAGS" | Just ldflags => pure ldflags - Nothing <- getEnv "LDFLAGS" + Nothing <- idrisGetEnv "LDFLAGS" | Just ldflags => pure ldflags pure "" +findLDLIBS : IO String +findLDLIBS + = do Nothing <- idrisGetEnv "IDRIS2_LDLIBS" + | Just ldlibs => pure ldlibs + Nothing <- idrisGetEnv "LDLIBS" + | Just ldlibs => pure ldlibs + pure "" + clibdirs : List String -> List String clibdirs ds = map (\d => "-L" ++ d) ds @@ -63,11 +74,13 @@ compileCObjectFile {asLibrary} sourceFile objectFile = let libraryFlag = if asLibrary then ["-fpic"] else [] - let runccobj = escapeCmd $ + let runccobj = (escapeCmd $ [cc, "-Werror", "-c"] ++ libraryFlag ++ [sourceFile, "-o", objectFile, "-I" ++ refcDir, - "-I" ++ cDir] + "-I" ++ cDir]) + ++ " " ++ cppFlags ++ " " ++ cFlags + log "compiler.refc.cc" 10 runccobj 0 <- coreLift $ system runccobj @@ -85,6 +98,7 @@ compileCFile {asShared} objectFile outFile = do cc <- coreLift findCC cFlags <- coreLift findCFLAGS ldFlags <- coreLift findLDFLAGS + ldLibs <- coreLift findLDLIBS dirs <- getDirs refcDir <- findDataFile "refc" @@ -92,14 +106,15 @@ compileCFile {asShared} objectFile outFile = let sharedFlag = if asShared then ["-shared"] else [] - let runcc = escapeCmd $ + let runcc = (escapeCmd $ [cc, "-Werror"] ++ sharedFlag ++ [objectFile, "-o", outFile, supportFile, "-lidris2_refc", "-L" ++ refcDir ] ++ clibdirs (lib_dirs dirs) ++ [ - "-lgmp", "-lm"] + "-lgmp", "-lm"]) + ++ " " ++ (unwords [cFlags, ldFlags, ldLibs]) log "compiler.refc.cc" 10 runcc 0 <- coreLift $ system runcc diff --git a/src/Idris/Env.idr b/src/Idris/Env.idr index e05a79ae4c..3a95d95dc1 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 0000000000..f3c476f231 --- /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]]) diff --git a/tests/refc/ccompilerArgs/expected b/tests/refc/ccompilerArgs/expected new file mode 100644 index 0000000000..f3e94419ea --- /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 0000000000..528a173fc6 --- /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 0000000000..f76e75fe37 --- /dev/null +++ b/tests/refc/ccompilerArgs/library/externalc.c @@ -0,0 +1,17 @@ +#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 0000000000..4d136bddeb --- /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 0000000000..c22a3523be --- /dev/null +++ b/tests/refc/ccompilerArgs/run @@ -0,0 +1,31 @@ +. ../../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,-S` removes debugging information +# from 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,-S" +export LDLIBS="-lexternalc" +export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:./library/" +export DYLD_LIBRARY_PATH="$DYLD_LIBRARY_PATH:./library/" + +idris2 --cg refc -o cffi Main.idr > /dev/null + +./build/exec/cffi \ No newline at end of file