Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow c_flags, c_names, cxx_names in executable stanzas #2562

Merged
merged 5 commits into from
Aug 20, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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