1
0
mirror of https://github.com/janet-lang/janet synced 2025-07-05 03:22:54 +00:00

For #469 - Add support for C++ and mixed C/C++

WIP and for native modules. Required a few changes to headers and
some changes to JPM.
This commit is contained in:
Calvin Rose 2020-09-04 17:05:28 -05:00
parent 39032b45c9
commit 25156eb83e
6 changed files with 166 additions and 24 deletions

112
jpm
View File

@ -323,7 +323,9 @@
# #
(def default-compiler (or (os/getenv "CC") (if is-win "cl.exe" "cc"))) (def default-compiler (or (os/getenv "CC") (if is-win "cl.exe" "cc")))
(def default-cpp-compiler (or (os/getenv "CXX") (if is-win "cl.exe" "c++")))
(def default-linker (or (os/getenv "CC") (if is-win "link.exe" "cc"))) (def default-linker (or (os/getenv "CC") (if is-win "link.exe" "cc")))
(def default-cpp-linker (or (os/getenv "CXX") (if is-win "link.exe" "c++")))
(def default-archiver (or (os/getenv "AR") (if is-win "lib.exe" "ar"))) (def default-archiver (or (os/getenv "AR") (if is-win "lib.exe" "ar")))
# Detect threads # Detect threads
@ -352,6 +354,10 @@
(if is-win (if is-win
["/nologo" "/MD"] ["/nologo" "/MD"]
["-std=c99" "-Wall" "-Wextra"])) ["-std=c99" "-Wall" "-Wextra"]))
(def default-cppflags
(if is-win
["/nologo" "/MD"]
["-std=c++11" "-Wall" "-Wextra"]))
(def default-ldflags []) (def default-ldflags [])
# Required flags for dynamic libraries. These # Required flags for dynamic libraries. These
@ -424,6 +430,13 @@
(string "-I" (dyn :headerpath JANET_HEADERPATH)) (string "-I" (dyn :headerpath JANET_HEADERPATH))
(string "-O" (opt opts :optimize 2))]) (string "-O" (opt opts :optimize 2))])
(defn- getcppflags
"Generate the cpp flags from the input options."
[opts]
@[;(opt opts :cppflags default-cppflags)
(string "-I" (dyn :headerpath JANET_HEADERPATH))
(string "-O" (opt opts :optimize 2))])
(defn- entry-name (defn- entry-name
"Name of symbol that enters static compilation of a module." "Name of symbol that enters static compilation of a module."
[name] [name]
@ -441,12 +454,30 @@
(def headers (or (opts :headers) [])) (def headers (or (opts :headers) []))
(rule dest [src ;headers] (rule dest [src ;headers]
(check-cc) (check-cc)
(print "compiling " dest "...") (print "compiling " src " to " dest "...")
(create-dirs dest) (create-dirs dest)
(if is-win (if is-win
(shell cc ;defines "/c" ;cflags (string "/Fo" dest) src) (shell cc ;defines "/c" ;cflags (string "/Fo" dest) src)
(shell cc "-c" src ;defines ;cflags "-o" dest)))) (shell cc "-c" src ;defines ;cflags "-o" dest))))
(defn- compile-cpp
"Compile a C++ file into an object file."
[opts src dest &opt static?]
(def cpp (opt opts :cpp-compiler default-cpp-compiler))
(def cflags [;(getcppflags opts) ;(if static? [] dynamic-cflags)])
(def entry-defines (if-let [n (opts :entry-name)]
[(make-define "JANET_ENTRY_NAME" n)]
[]))
(def defines [;(make-defines (opt opts :defines {})) ;entry-defines])
(def headers (or (opts :headers) []))
(rule dest [src ;headers]
(check-cc)
(print "compiling " src " to " dest "...")
(create-dirs dest)
(if is-win
(shell cpp ;defines "/c" ;cflags (string "/Fo" dest) src)
(shell cpp "-c" src ;defines ;cflags "-o" dest))))
(defn- libjanet (defn- libjanet
"Find libjanet.a (or libjanet.lib on windows) at compile time" "Find libjanet.a (or libjanet.lib on windows) at compile time"
[] []
@ -466,7 +497,7 @@
(string hpath `\\janet.lib`)) (string hpath `\\janet.lib`))
(defn- link-c (defn- link-c
"Link object files together to make a native module." "Link C object files together to make a native module."
[opts target & objects] [opts target & objects]
(def linker (opt opts (if is-win :linker :compiler) default-linker)) (def linker (opt opts (if is-win :linker :compiler) default-linker))
(def cflags (getcflags opts)) (def cflags (getcflags opts))
@ -481,6 +512,22 @@
(shell linker ;ldflags (string "/OUT:" target) ;objects (win-import-library) ;lflags) (shell linker ;ldflags (string "/OUT:" target) ;objects (win-import-library) ;lflags)
(shell linker ;cflags ;ldflags `-o` target ;objects ;lflags)))) (shell linker ;cflags ;ldflags `-o` target ;objects ;lflags))))
(defn- link-cpp
"Link C++ object files together to make a native module."
[opts target & objects]
(def linker (opt opts (if is-win :cpp-linker :cpp-compiler) default-cpp-linker))
(def cflags (getcppflags opts))
(def lflags [;(opt opts :lflags default-lflags)
;(if (opts :static) [] dynamic-lflags)])
(def ldflags [;(opt opts :ldflags [])])
(rule target objects
(check-cc)
(print "linking " target "...")
(create-dirs target)
(if is-win
(shell linker ;ldflags (string "/OUT:" target) ;objects (win-import-library) ;lflags)
(shell linker ;cflags ;ldflags `-o` target ;objects ;lflags))))
(defn- archive-c (defn- archive-c
"Link object files together to make a static library." "Link object files together to make a static library."
[opts target & objects] [opts target & objects]
@ -655,10 +702,12 @@ int main(int argc, const char **argv) {
(table/setproto m oldproto)) (table/setproto m oldproto))
# Find static modules # Find static modules
(var has-cpp false)
(def declarations @"") (def declarations @"")
(def lookup-into-invocations @"") (def lookup-into-invocations @"")
(loop [[prefix name] :pairs prefixes] (loop [[prefix name] :pairs prefixes]
(def meta (eval-string (slurp (modpath-to-meta name)))) (def meta (eval-string (slurp (modpath-to-meta name))))
(if (meta :cpp) (set has-cpp true))
(buffer/push-string lookup-into-invocations (buffer/push-string lookup-into-invocations
" temptab = janet_table(0);\n" " temptab = janet_table(0);\n"
" temptab->proto = env;\n" " temptab->proto = env;\n"
@ -683,15 +732,24 @@ int main(int argc, const char **argv) {
(spit cimage_dest (make-bin-source declarations lookup-into-invocations) :ab) (spit cimage_dest (make-bin-source declarations lookup-into-invocations) :ab)
# Compile and link final exectable # Compile and link final exectable
(unless no-compile (unless no-compile
(def cc (opt opts :compiler default-compiler))
(def ldflags [;dep-ldflags ;(opt opts :ldflags []) ;janet-ldflags]) (def ldflags [;dep-ldflags ;(opt opts :ldflags []) ;janet-ldflags])
(def lflags [;static-libs (libjanet) ;dep-lflags ;(opt opts :lflags default-lflags) ;janet-lflags]) (def lflags [;static-libs (libjanet) ;dep-lflags ;(opt opts :lflags default-lflags) ;janet-lflags])
(def cflags [;(getcflags opts) ;janet-cflags])
(def defines (make-defines (opt opts :defines {}))) (def defines (make-defines (opt opts :defines {})))
(if has-cpp
(do
(def cc (opt opts :cpp-compiler default-cpp-compiler))
(def cflags [;(getcppflags opts) ;janet-cflags])
(print "compiling and linking " dest "...") (print "compiling and linking " dest "...")
(if is-win (if is-win
(shell cc ;cflags ;ldflags cimage_dest ;lflags `/link` (string "/OUT:" dest)) (shell cc ;cflags ;ldflags cimage_dest ;lflags `/link` (string "/OUT:" dest))
(shell cc ;cflags ;ldflags `-o` dest cimage_dest ;lflags))))) (shell cc ;cflags ;ldflags `-o` dest cimage_dest ;lflags)))
(do
(def cc (opt opts :compiler default-compiler))
(def cflags [;(getcflags opts) ;janet-cflags])
(print "compiling and linking " dest "...")
(if is-win
(shell cc ;cflags ;ldflags cimage_dest ;lflags `/link` (string "/OUT:" dest))
(shell cc ;cflags ;ldflags `-o` dest cimage_dest ;lflags)))))))
# #
# Installation and Dependencies # Installation and Dependencies
@ -853,9 +911,23 @@ int main(int argc, const char **argv) {
# Make dynamic module # Make dynamic module
(def lname (string "build" sep name modext)) (def lname (string "build" sep name modext))
(loop [src :in sources]
(compile-c opts src (out-path src ".c" objext))) # Get objects to build with
(def objects (map (fn [path] (out-path path ".c" objext)) sources)) (var has-cpp false)
(def objects
(seq [src :in sources]
(cond
(string/has-suffix? ".cpp" src)
(let [op (out-path src ".cpp" objext)]
(compile-cpp opts src op)
(set has-cpp true)
op)
(string/has-suffix? ".c" src)
(let [op (out-path src ".c" objext)]
(compile-c opts src op)
op)
(errorf "unknown source file type: %s, expected .c or .cpp"))))
(when-let [embedded (opts :embedded)] (when-let [embedded (opts :embedded)]
(loop [src :in embedded] (loop [src :in embedded]
(def c-src (out-path src ".janet" ".janet.c")) (def c-src (out-path src ".janet" ".janet.c"))
@ -863,7 +935,7 @@ int main(int argc, const char **argv) {
(array/push objects o-src) (array/push objects o-src)
(create-buffer-c src c-src (embed-name src)) (create-buffer-c src c-src (embed-name src))
(compile-c opts c-src o-src))) (compile-c opts c-src o-src)))
(link-c opts lname ;objects) ((if has-cpp link-cpp link-c) opts lname ;objects)
(add-dep "build" lname) (add-dep "build" lname)
(install-rule lname path) (install-rule lname path)
@ -876,6 +948,7 @@ int main(int argc, const char **argv) {
"# Metadata for static library %s\n\n%.20p" "# Metadata for static library %s\n\n%.20p"
(string name statext) (string name statext)
{:static-entry ename {:static-entry ename
:cpp has-cpp
:ldflags ~',(opts :ldflags) :ldflags ~',(opts :ldflags)
:lflags ~',(opts :lflags)}))) :lflags ~',(opts :lflags)})))
(add-dep "build" metaname) (add-dep "build" metaname)
@ -887,9 +960,21 @@ int main(int argc, const char **argv) {
(def opts (merge @{:entry-name ename} opts)) (def opts (merge @{:entry-name ename} opts))
(def sobjext (string ".static" objext)) (def sobjext (string ".static" objext))
(def sjobjext (string ".janet" sobjext)) (def sjobjext (string ".janet" sobjext))
(loop [src :in sources]
(compile-c opts src (out-path src ".c" sobjext) true)) # Get static objects
(def sobjects (map (fn [path] (out-path path ".c" sobjext)) sources)) (def sobjects
(seq [src :in sources]
(cond
(string/has-suffix? ".cpp" src)
(let [op (out-path src ".cpp" sobjext)]
(compile-cpp opts src op true)
op)
(string/has-suffix? ".c" src)
(let [op (out-path src ".c" sobjext)]
(compile-c opts src op true)
op)
(errorf "unknown source file type: %s, expected .c or .cpp"))))
(when-let [embedded (opts :embedded)] (when-let [embedded (opts :embedded)]
(loop [src :in embedded] (loop [src :in embedded]
(def c-src (out-path src ".janet" ".janet.c")) (def c-src (out-path src ".janet" ".janet.c"))
@ -1139,7 +1224,8 @@ Keys are:
--binpath : The directory to install binaries and scripts. Defaults to $JANET_BINPATH. --binpath : The directory to install binaries and scripts. Defaults to $JANET_BINPATH.
--libpath : The directory containing janet C libraries (libjanet.*). Defaults to $JANET_LIBPATH. --libpath : The directory containing janet C libraries (libjanet.*). Defaults to $JANET_LIBPATH.
--compiler : C compiler to use for natives. Defaults to $CC or cc (cl.exe on windows). --compiler : C compiler to use for natives. Defaults to $CC or cc (cl.exe on windows).
--archiver : C compiler to use for static libraries. Defaults to $AR ar (lib.exe on windows). --cpp-compiler : C++ compiler to use for natives. Defaults to $CXX or c++ (cl.exe on windows).
--archiver : C archiver to use for static libraries. Defaults to $AR ar (lib.exe on windows).
--linker : C linker to use for linking natives. Defaults to link.exe on windows, not used on --linker : C linker to use for linking natives. Defaults to link.exe on windows, not used on
other platforms. other platforms.
--pkglist : URL of git repository for package listing. Defaults to $JANET_PKGLIST or https://github.com/janet-lang/pkgs.git --pkglist : URL of git repository for package listing. Defaults to $JANET_PKGLIST or https://github.com/janet-lang/pkgs.git

6
jpm.1
View File

@ -71,9 +71,13 @@ $JANET_LIBPATH, or a reasonable default. See JANET_LIBPATH for more.
.TP .TP
.BR \-\-compiler=$CC .BR \-\-compiler=$CC
Sets the compiler used for compiling native modules and standalone executables. Defaults Sets the C compiler used for compiling native modules and standalone executables. Defaults
to cc. to cc.
.BR \-\-cpp\-compiler=$CXX
Sets the C++ compiler used for compiling native modules and standalone executables. Defaults
to c++..
.TP .TP
.BR \-\-linker .BR \-\-linker
Sets the linker used to create native modules and executables. Only used on windows, where Sets the linker used to create native modules and executables. Only used on windows, where

View File

@ -201,7 +201,7 @@ extern "C" {
#ifdef JANET_WINDOWS #ifdef JANET_WINDOWS
#define JANET_NO_RETURN __declspec(noreturn) #define JANET_NO_RETURN __declspec(noreturn)
#else #else
#define JANET_NO_RETURN __attribute__ ((noreturn)) #define JANET_NO_RETURN __attribute__((noreturn))
#endif #endif
#endif #endif
@ -561,7 +561,7 @@ JANET_API Janet janet_wrap_integer(int32_t x);
#define janet_nanbox_tag(type) (janet_nanbox_lowtag(type) << 47) #define janet_nanbox_tag(type) (janet_nanbox_lowtag(type) << 47)
#define janet_type(x) \ #define janet_type(x) \
(isnan((x).number) \ (isnan((x).number) \
? (((x).u64 >> 47) & 0xF) \ ? (JanetType) (((x).u64 >> 47) & 0xF) \
: JANET_NUMBER) : JANET_NUMBER)
#define janet_nanbox_checkauxtype(x, type) \ #define janet_nanbox_checkauxtype(x, type) \
@ -640,7 +640,7 @@ JANET_API Janet janet_nanbox_from_bits(uint64_t bits);
#define JANET_DOUBLE_OFFSET 0xFFFF #define JANET_DOUBLE_OFFSET 0xFFFF
#define janet_u64(x) ((x).u64) #define janet_u64(x) ((x).u64)
#define janet_type(x) (((x).tagged.type < JANET_DOUBLE_OFFSET) ? (x).tagged.type : JANET_NUMBER) #define janet_type(x) (((x).tagged.type < JANET_DOUBLE_OFFSET) ? (JanetType)((x).tagged.type) : JANET_NUMBER)
#define janet_checktype(x, t) ((t) == JANET_NUMBER \ #define janet_checktype(x, t) ((t) == JANET_NUMBER \
? (x).tagged.type >= JANET_DOUBLE_OFFSET \ ? (x).tagged.type >= JANET_DOUBLE_OFFSET \
: (x).tagged.type == (t)) : (x).tagged.type == (t))
@ -1449,14 +1449,19 @@ JANET_API Janet janet_resolve_core(const char *name);
/* New C API */ /* New C API */
/* Allow setting entry name for static libraries */ /* Allow setting entry name for static libraries */
#ifdef __cplusplus
#define JANET_MODULE_PREFIX extern "C"
#else
#define JANET_MODULE_PREFIX
#endif
#ifndef JANET_ENTRY_NAME #ifndef JANET_ENTRY_NAME
#define JANET_MODULE_ENTRY \ #define JANET_MODULE_ENTRY \
JANET_API JanetBuildConfig _janet_mod_config(void) { \ JANET_MODULE_PREFIX JANET_API JanetBuildConfig _janet_mod_config(void) { \
return janet_config_current(); \ return janet_config_current(); \
} \ } \
JANET_API void _janet_init JANET_MODULE_PREFIX JANET_API void _janet_init
#else #else
#define JANET_MODULE_ENTRY JANET_API void JANET_ENTRY_NAME #define JANET_MODULE_ENTRY JANET_MODULE_PREFIX JANET_API void JANET_ENTRY_NAME
#endif #endif
JANET_NO_RETURN JANET_API void janet_signalv(JanetSignal signal, Janet message); JANET_NO_RETURN JANET_API void janet_signalv(JanetSignal signal, Janet message);

View File

@ -9,6 +9,10 @@
:name "testmod2" :name "testmod2"
:source @["testmod2.c"]) :source @["testmod2.c"])
(declare-native
:name "testmod3"
:source @["testmod3.cpp"])
(declare-executable (declare-executable
:name "testexec" :name "testexec"
:entry "testexec.janet") :entry "testexec.janet")

View File

@ -1,6 +1,7 @@
(use build/testmod) (use build/testmod)
(use build/testmod2) (use build/testmod2)
(use build/testmod3)
(defn main [&] (defn main [&]
(print "Hello from executable!") (print "Hello from executable!")
(print (+ (get5) (get6)))) (print (+ (get5) (get6) (get7))))

42
test/install/testmod3.cpp Normal file
View File

@ -0,0 +1,42 @@
/*
* Copyright (c) 2020 Calvin Rose and contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
/* A very simple native module */
#include <janet.h>
#include <iostream>
static Janet cfun_get_seven(int32_t argc, Janet *argv) {
(void) argv;
janet_fixarity(argc, 0);
std::cout << "Hello!" << std::endl;
return janet_wrap_number(7.0);
}
static const JanetReg array_cfuns[] = {
{"get7", cfun_get_seven, NULL},
{NULL, NULL, NULL}
};
JANET_MODULE_ENTRY(JanetTable *env) {
janet_cfuns(env, NULL, array_cfuns);
}