Skip to content

Commit

Permalink
Merge pull request #2562 from nojb/executables_c_names
Browse files Browse the repository at this point in the history
Allow c_flags, c_names, cxx_names in executable stanzas
  • Loading branch information
nojb authored Aug 20, 2019
2 parents 80fc7ea + 0e0d875 commit f498267
Show file tree
Hide file tree
Showing 21 changed files with 328 additions and 167 deletions.
3 changes: 3 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,9 @@
- Fix invalid library names in `dune-package` files. Only public names should
exist in such files. (#2558, fix #2425, @rgrinberg)

- `c_flags`, `c_names` and `cxx_names` are now supported in `executable`
and `executables` stanzas. (#2562, @nojb)

1.11.0 (23/07/2019)
-------------------

Expand Down
13 changes: 13 additions & 0 deletions doc/dune-files.rst
Original file line number Diff line number Diff line change
Expand Up @@ -595,6 +595,16 @@ Executables can also be linked as object or shared object files. See
``executable`` stanza will cause Dune to copy the ``.exe`` files to
the source tree and ``dune clean`` to delete them

- ``(c_names (<names>))``, if your executable needs C stubs, you must list the C
files in this field, without the ``.c`` extension

- ``(cxx_names (<names>))`` is the same as ``c_names`` but for C++ stubs

- ``(c_flags <flags>)`` specifies the compilation flags for C stubs, using the
:ref:`ordered-set-language`. This field supports ``(:include ...)`` forms

- ``(cxx_flags <flags>)`` is the same as ``c_flags`` but for C++ stubs

Linking modes
~~~~~~~~~~~~~

Expand Down Expand Up @@ -676,6 +686,9 @@ version is the same as the ``.bc`` one except that it is linked with
the ``-custom`` option of the compiler. You should always use the
``.exe`` rather that the ``.bc`` inside build rules.

Lastly, note that ``.bc`` executables cannot contain C stubs. If your executable
contains C stubs you may want to use ``(modes exe)``.

executables
-----------

Expand Down
88 changes: 88 additions & 0 deletions src/dune/c_rules.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
open! Stdune
open Dune_file

let build_c_file (buildable : Buildable.t) ~sctx ~dir ~expander ~includes
(loc, src, dst) =
let ctx = Super_context.context sctx in
let c_flags =
(Super_context.c_flags sctx ~dir ~expander ~flags:buildable.c_flags).c
in
Super_context.add_rule sctx ~loc
~dir
(* With sandboxing we get errors like: bar.c:2:19: fatal error: foo.cxx:
No such file or directory #include "foo.cxx" *)
~sandbox:Sandbox_config.no_sandboxing
(let src = Path.build (C.Source.path src) in
Command.run
(* We have to execute the rule in the library directory as the .o is
produced in the current directory *) ~dir:(Path.build dir)
(Ok ctx.ocamlc)
[ A "-g"
; includes
; Dyn (Build.S.map c_flags ~f:(fun x -> Command.quote_args "-ccopt" x))
; A "-o"
; Target dst
; Dep src
]);
dst

let build_cxx_file (buildable : Buildable.t) ~sctx ~dir ~expander ~includes
(loc, src, dst) =
let ctx = Super_context.context sctx in
let output_param =
if ctx.ccomp_type = "msvc" then
[ Command.Args.Concat ("", [ A "/Fo"; Target dst ]) ]
else
[ A "-o"; Target dst ]
in
let cxx_flags =
(Super_context.c_flags sctx ~dir ~expander ~flags:buildable.c_flags).cxx
in
Super_context.add_rule sctx ~loc
~dir
(* this seems to work with sandboxing, but for symmetry with
[build_c_file] disabling that here too *)
~sandbox:Sandbox_config.no_sandboxing
(let src = Path.build (C.Source.path src) in
Command.run
(* We have to execute the rule in the library directory as the .o is
produced in the current directory *) ~dir:(Path.build dir)
(Super_context.resolve_program ~loc:None ~dir sctx ctx.c_compiler)
( [ Command.Args.S [ A "-I"; Path ctx.stdlib_dir ]
; includes
; Command.Args.dyn cxx_flags
]
@ output_param @ [ A "-c"; Dep src ] ));
dst

let build_o_files buildable ~sctx ~(c_sources : C.Sources.t) ~dir ~expander
~requires ~dir_contents =
let ctx = Super_context.context sctx in
let all_dirs = Dir_contents.dirs dir_contents in
let h_files =
List.fold_left all_dirs ~init:[] ~f:(fun acc dc ->
String.Set.fold (Dir_contents.text_files dc) ~init:acc ~f:(fun fn acc ->
if String.is_suffix fn ~suffix:C.header_ext then
Path.relative (Path.build (Dir_contents.dir dc)) fn :: acc
else
acc))
in
let includes =
Command.Args.S
[ Hidden_deps (Dep.Set.of_files h_files)
; Command.of_result_map requires ~f:(fun libs ->
S
[ Lib.L.c_include_flags libs
; Hidden_deps
(Lib_file_deps.deps libs ~groups:[ Lib_file_deps.Group.Header ])
])
]
in
let build_x_files build_x files =
String.Map.to_list files
|> List.map ~f:(fun (obj, (loc, src)) ->
let dst = Path.Build.relative dir (obj ^ ctx.lib_config.ext_obj) in
build_x buildable ~sctx ~dir ~expander ~includes (loc, src, dst))
in
let { C.Kind.Dict.c; cxx } = C.Sources.split_by_kind c_sources in
build_x_files build_c_file c @ build_x_files build_cxx_file cxx
13 changes: 13 additions & 0 deletions src/dune/c_rules.mli
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
open! Stdune
open Import
open Dune_file

val build_o_files :
Buildable.t
-> sctx:Super_context.t
-> c_sources:C.Sources.t
-> dir:Path.Build.t
-> expander:Expander.t
-> requires:Lib.L.t Or_exn.t
-> dir_contents:Dir_contents.t
-> Path.Build.t list
118 changes: 65 additions & 53 deletions src/dune/c_sources.ml
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,16 @@ open Stdune
open Dune_file
module Library = Dune_file.Library

type t = { libraries : C.Sources.t Lib_name.Map.t }
type t =
{ libraries : C.Sources.t Lib_name.Map.t
; executables : C.Sources.t String.Map.t
}

let for_lib t ~name = Lib_name.Map.find_exn t.libraries name

let empty = { libraries = Lib_name.Map.empty }
let for_exes t ~first_exe = String.Map.find_exn t.executables first_exe

let empty = { libraries = Lib_name.Map.empty; executables = String.Map.empty }

let c_name, cxx_name =
let make what ~loc s =
Expand Down Expand Up @@ -42,60 +47,63 @@ let load_sources ~dune_version ~dir ~files =
C.Kind.Dict.update acc kind ~f:(fun v ->
String.Map.set v obj (C.Source.make ~kind ~path)))

let eval_c_sources (d : _ Dir_with_dune.t) buildable ~c_sources =
let eval (kind : C.Kind.t) (c_sources : C.Source.t String.Map.t) validate osl
=
Ordered_set_lang.Unordered_string.eval_loc osl
~key:(fun x -> x)
~parse:(fun ~loc s ->
let s = validate ~loc s in
let s' = Filename.basename s in
if s' <> s then
User_error.raise ~loc
[ Pp.text
"relative part of stub is not necessary and should be removed. \
To include sources in subdirectories, use the include_subdirs \
stanza"
];
s')
~standard:String.Map.empty
|> String.Map.map ~f:(fun (loc, s) ->
match String.Map.find c_sources s with
| Some source -> (loc, source)
| None ->
let dune_version = d.dune_version in
User_error.raise ~loc
[ Pp.textf "%s does not exist as a C source. %s must be present" s
(String.enumerate_one_of (C.Kind.possible_fns kind s ~dune_version))
])
in
let names = Option.value ~default:Ordered_set_lang.standard in
let c =
eval C.Kind.C c_sources.C.Kind.Dict.c c_name
(names buildable.Buildable.c_names)
in
let cxx =
eval C.Kind.Cxx c_sources.cxx cxx_name (names buildable.cxx_names)
in
String.Map.union c cxx ~f:(fun _ (_loc1, c) (loc2, cxx) ->
User_error.raise ~loc:loc2
[ Pp.textf
"%s and %s have conflicting names. You must rename one of them."
(Path.to_string_maybe_quoted
(Path.drop_optional_build_context (Path.build (C.Source.path cxx))))
(Path.to_string_maybe_quoted
(Path.drop_optional_build_context (Path.build (C.Source.path c))))
])

let make (d : _ Dir_with_dune.t)
~(c_sources : C.Source.t String.Map.t C.Kind.Dict.t) =
let libs =
List.filter_map d.data ~f:(fun stanza ->
let libs, exes =
List.filter_partition_map d.data ~f:(fun stanza ->
match (stanza : Stanza.t) with
| Library lib ->
let eval (kind : C.Kind.t) (c_sources : C.Source.t String.Map.t)
validate osl =
Ordered_set_lang.Unordered_string.eval_loc osl
~key:(fun x -> x)
~parse:(fun ~loc s ->
let s = validate ~loc s in
let s' = Filename.basename s in
if s' <> s then
User_error.raise ~loc
[ Pp.text
"relative part of stub is not necessary and should be \
removed. To include sources in subdirectories, use the \
include_subdirs stanza"
];
s')
~standard:String.Map.empty
|> String.Map.map ~f:(fun (loc, s) ->
match String.Map.find c_sources s with
| Some source -> (loc, source)
| None ->
let dune_version = d.dune_version in
User_error.raise ~loc
[ Pp.textf
"%s does not exist as a C source. %s must be present" s
(String.enumerate_one_of
(C.Kind.possible_fns kind s ~dune_version))
])
in
let names = Option.value ~default:Ordered_set_lang.standard in
let c = eval C.Kind.C c_sources.c c_name (names lib.c_names) in
let cxx =
eval C.Kind.Cxx c_sources.cxx cxx_name (names lib.cxx_names)
in
let all =
String.Map.union c cxx ~f:(fun _ (_loc1, c) (loc2, cxx) ->
User_error.raise ~loc:loc2
[ Pp.textf
"%s and %s have conflicting names. You must rename one of them."
(Path.to_string_maybe_quoted
(Path.drop_optional_build_context
(Path.build (C.Source.path cxx))))
(Path.to_string_maybe_quoted
(Path.drop_optional_build_context
(Path.build (C.Source.path c))))
])
in
Some (lib, all)
| _ -> None)
let all = eval_c_sources d lib.buildable ~c_sources in
Left (lib, all)
| Executables exes ->
let all = eval_c_sources d exes.buildable ~c_sources in
Right (exes, all)
| _ -> Skip)
in
let libraries =
match
Expand All @@ -109,6 +117,10 @@ let make (d : _ Dir_with_dune.t)
(Lib_name.to_string name)
]
in
let executables =
String.Map.of_list_map_exn exes ~f:(fun (exes, m) ->
(snd (List.hd exes.names), m))
in
let () =
let rev_map =
List.concat_map libs ~f:(fun (_, c_sources) ->
Expand All @@ -124,4 +136,4 @@ let make (d : _ Dir_with_dune.t)
; Pp.textf "- %s" (Loc.to_file_colon_line loc1)
]
in
{ libraries }
{ libraries; executables }
2 changes: 2 additions & 0 deletions src/dune/c_sources.mli
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ val empty : t

val for_lib : t -> name:Lib_name.t -> C.Sources.t

val for_exes : t -> first_exe:string -> C.Sources.t

(** [load_sources dir ~files] will load the C sources in [dir] into a two
double map. The first level will is keyed by C vs. C++ sources. The second
level is keyed by the object name of the source. *)
Expand Down
3 changes: 3 additions & 0 deletions src/dune/dir_contents.ml
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@ let modules_of_executables t ~obj_dir ~first_exe =
let src_dir = Path.build (Obj_dir.obj_dir obj_dir) in
String.Map.find_exn map first_exe |> Modules.relocate_alias_module ~src_dir

let c_sources_of_executables t ~first_exe =
C_sources.for_exes (Memo.Lazy.force t.c_sources) ~first_exe

let c_sources_of_library t ~name =
C_sources.for_lib (Memo.Lazy.force t.c_sources) ~name

Expand Down
2 changes: 2 additions & 0 deletions src/dune/dir_contents.mli
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ val c_sources_of_library : t -> name:Lib_name.t -> C.Sources.t
val modules_of_executables :
t -> obj_dir:Path.Build.t Obj_dir.t -> first_exe:string -> Modules.t

val c_sources_of_executables : t -> first_exe:string -> C.Sources.t

(** Find out what buildable a module is part of *)
val lookup_module : t -> Module_name.t -> Dune_file.Buildable.t option

Expand Down
Loading

0 comments on commit f498267

Please sign in to comment.