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

Enable/disable bisect_ppx via dune-workspace #3403

Merged
7 commits merged into from
May 6, 2020
Merged
Show file tree
Hide file tree
Changes from 4 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: 2 additions & 1 deletion .travis-ci.sh
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ opam_install_test_deps () {
ocaml-migrate-parsetree \
result.1.4 \
utop.2.4.2 \
mdx.1.6.0
mdx.1.6.0 \
bisect_ppx
# We install Coq separatedly as to be more resistant w.r.t. the 10
# minutes Travis timeout; the travis_wait hack doesn't work well
# with Dune's current setup. Note that Travis caching should help
Expand Down
6 changes: 6 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
Unreleased
------------------
- Allow bisect_ppx to be enabled/disabled via dune-workspace. (#3404,
@stephanieyou)


2.5.1 (17/04/2020)
------------------

Expand Down
87 changes: 87 additions & 0 deletions doc/bisect.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
*************************
Code coverage with bisect
*************************

Normally, preprocessors (specified in ``dune`` files) are constant across all
builds. However, it usually doesn't make sense to analyze code coverage on all
builds, especially if you aren't running tests every time.
This conversation was marked as resolved.
Show resolved Hide resolved

In this section, we will explain how to set up code coverage with bisect_ppx_ so
that you can enable and disable coverage via ``dune-workspace`` files.

Specifying what to bisect
=========================

To measure coverage for every build, we can include ``bisect_ppx`` in the
``preprocess`` field for each relevant library and executable. For example, the
dune file would include stanza(s) like this:

.. code:: scheme

(library
(name foo)
(preprocess (pps bisect_ppx)))

This aligns with the standard usage of ppx libraries.
This conversation was marked as resolved.
Show resolved Hide resolved

However, if we would like to control which builds measure coverage, first we
must include ``(using bisect_ppx 1.0)`` in our ``dune-project`` file, like so:

.. code:: scheme

(lang dune 2.6)
(using bisect_ppx 1.0)

Then, instead of including ``bisect_ppx`` in ``preprocess``, we should use the
``(bisect_ppx)`` field. The dune file may look like this:

.. code:: scheme

(library
(name foo)
(modules foo)
(bisect_ppx))

(executable
(name test)
(modules test)
(libraries foo))

The ``(bisect_ppx)`` field can be specified in library and executable stanzas.
Libraries/executables that do not use ``(bisect_ppx)`` will not be instrumented
for code coverage.

Enabling/disabling code coverage
================================

By default, ``bisect_ppx`` is not compiled and linked with the program when
using ``(bisect_ppx)``. To enable code coverage, we can set the
``bisect_enabled`` flag in a ``dune-workspace`` file. For example,
``dune-workspace.dev`` may look like:

.. code:: scheme

(lang dune 2.6)
(context (default (bisect_enabled true)))

Then, to build the project with code coverage, we can run:

.. code:: bash

$ dune exec ./test.exe --workspace dune-workspace.dev

We can also define different contexts in the ``dune-workspace`` file as follows:

.. code:: scheme

(lang dune 2.6)
(context default)
(context (default (name coverage) (bisect_enabled true)))

Running the following will enable coverage:

.. code:: bash

$ dune exec ./test.exe --context coverage

.. _bisect_ppx: https://github.com/aantron/bisect_ppx
6 changes: 3 additions & 3 deletions doc/dune-files.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ like:

.. code:: scheme

(lang dune 2.5)
(lang dune 2.6)

Additionally, they can contains the following stanzas.

Expand Down Expand Up @@ -1748,7 +1748,7 @@ a typical ``dune-workspace`` file looks like:

.. code:: scheme

(lang dune 2.5)
(lang dune 2.6)
(context (opam (switch 4.02.3)))
(context (opam (switch 4.03.0)))
(context (opam (switch 4.04.0)))
Expand All @@ -1760,7 +1760,7 @@ containing exactly:

.. code:: scheme

(lang dune 2.5)
(lang dune 2.6)
(context default)

This allows you to use an empty ``dune-workspace`` file to mark the root of your
Expand Down
1 change: 1 addition & 0 deletions doc/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ Welcome to dune's documentation!
dune-files
concepts
tests
bisect
foreign-code
documentation
jsoo
Expand Down
2 changes: 1 addition & 1 deletion doc/opam.rst
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ configuration will tell ``dune`` to generate two opam files: ``cohttp.opam`` and

.. code:: scheme

(lang dune 2.5)
(lang dune 2.6)
(name cohttp)
; version field is optional
(version 1.0.0)
Expand Down
15 changes: 10 additions & 5 deletions src/dune/context.ml
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,7 @@ let write_dot_dune_dir ~build_dir ~ocamlc ~ocaml_config_vars =

let create ~(kind : Kind.t) ~path ~env ~env_nodes ~name ~merlin ~targets
~host_context ~host_toolchain ~profile ~fdo_target_exe
~dynamically_linked_foreign_archives =
~dynamically_linked_foreign_archives ~bisect_enabled =
let prog_not_found_in_path prog =
Utils.program_not_found prog ~context:name ~loc:None
in
Expand Down Expand Up @@ -485,6 +485,7 @@ let create ~(kind : Kind.t) ~path ~env ~env_nodes ~name ~merlin ~targets
; ccomp_type = Ocaml_config.ccomp_type ocfg
; profile
; ocaml_version = Ocaml_config.version_string ocfg
; bisect_enabled
}
in
if Option.is_some fdo_target_exe then
Expand Down Expand Up @@ -597,10 +598,10 @@ let extend_paths t ~env =
Env.extend ~vars env

let default ~merlin ~env_nodes ~env ~targets ~fdo_target_exe
~dynamically_linked_foreign_archives =
~dynamically_linked_foreign_archives ~bisect_enabled =
let path = Env.path env in
create ~kind:Default ~path ~env ~env_nodes ~merlin ~targets ~fdo_target_exe
~dynamically_linked_foreign_archives
~dynamically_linked_foreign_archives ~bisect_enabled

let opam_version =
let f opam =
Expand Down Expand Up @@ -631,7 +632,7 @@ let opam_version =

let create_for_opam ~root ~env ~env_nodes ~targets ~profile ~switch ~name
~merlin ~host_context ~host_toolchain ~fdo_target_exe
~dynamically_linked_foreign_archives =
~dynamically_linked_foreign_archives ~bisect_enabled =
let opam =
match Memo.Lazy.force opam with
| None -> Utils.program_not_found "opam" ~loc:None
Expand Down Expand Up @@ -682,6 +683,7 @@ let create_for_opam ~root ~env ~env_nodes ~targets ~profile ~switch ~name
~kind:(Opam { root; switch })
~profile ~targets ~path ~env ~env_nodes ~name ~merlin ~host_context
~host_toolchain ~fdo_target_exe ~dynamically_linked_foreign_archives
~bisect_enabled

let instantiate_context env (workspace : Workspace.t)
~(context : Workspace.Context.t) ~host_context =
Expand All @@ -701,6 +703,7 @@ let instantiate_context env (workspace : Workspace.t)
; loc = _
; fdo_target_exe
; dynamically_linked_foreign_archives
; bisect_enabled
} ->
let merlin =
workspace.merlin_context = Some (Workspace.Context.name context)
Expand All @@ -716,6 +719,7 @@ let instantiate_context env (workspace : Workspace.t)
let env = extend_paths ~env paths in
default ~env ~env_nodes ~profile ~targets ~name ~merlin ~host_context
~host_toolchain ~fdo_target_exe ~dynamically_linked_foreign_archives
~bisect_enabled
| Opam
{ base =
{ targets
Expand All @@ -728,6 +732,7 @@ let instantiate_context env (workspace : Workspace.t)
; loc = _
; fdo_target_exe
; dynamically_linked_foreign_archives
; bisect_enabled
}
; switch
; root
Expand All @@ -736,7 +741,7 @@ let instantiate_context env (workspace : Workspace.t)
let env = extend_paths ~env paths in
create_for_opam ~root ~env_nodes ~env ~profile ~switch ~name ~merlin
~targets ~host_context ~host_toolchain:toolchain ~fdo_target_exe
~dynamically_linked_foreign_archives
~dynamically_linked_foreign_archives ~bisect_enabled

module Create = struct
module Output = struct
Expand Down
54 changes: 53 additions & 1 deletion src/dune/dune_file.ml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,14 @@ let variants_field =
(let* () = Dune_lang.Syntax.since library_variants (0, 1) in
located (repeat Variant.decode >>| Variant.Set.of_list))

let bisect_ppx_syntax =
Dune_lang.Syntax.create ~name:"bisect_ppx" ~desc:"the bisect_ppx extension"
[ ((1, 0), `Since (2, 6)) ]

let () =
Dune_project.Extension.register_simple bisect_ppx_syntax
(Dune_lang.Decoder.return [])

module Pps_and_flags = struct
let decode =
let+ l, flags =
Expand Down Expand Up @@ -214,6 +222,29 @@ module Preprocess_map = struct
List.fold_left (Preprocess.pps pp) ~init:acc ~f:(fun acc (loc, pp) ->
Lib_name.Map.set acc pp loc))
|> Lib_name.Map.foldi ~init:[] ~f:(fun pp loc acc -> (loc, pp) :: acc)

let add_bisect t =
let bisect_ppx =
let bisect_name = Lib_name.parse_string_exn (Loc.none, "bisect_ppx") in
(Loc.none, bisect_name)
in
Per_module.map t ~f:(fun pp ->
match pp with
| Preprocess.No_preprocessing ->
let loc = Loc.none in
let pps = [ bisect_ppx ] in
let flags = [] in
let staged = false in
Preprocess.Pps { loc; pps; flags; staged }
| Preprocess.Pps { loc; pps; flags; staged } ->
let pps = bisect_ppx :: pps in
Preprocess.Pps { loc; pps; flags; staged }
| Action (loc, _) | Future_syntax loc ->
User_error.raise ~loc
[ Pp.text
"Preprocessing with actions and future syntax cannot be used \
in conjunction with (bisect_ppx)"
])
end

module Lint = struct
Expand Down Expand Up @@ -359,6 +390,7 @@ module Buildable = struct
; flags : Ocaml_flags.Spec.t
; js_of_ocaml : Js_of_ocaml.t
; allow_overlapping_dependencies : bool
; bisect_ppx : bool
}

let decode ~in_library ~allow_re_export =
Expand Down Expand Up @@ -424,6 +456,9 @@ module Buildable = struct
field "js_of_ocaml" Js_of_ocaml.decode ~default:Js_of_ocaml.default
and+ allow_overlapping_dependencies =
field_b "allow_overlapping_dependencies"
and+ bisect_ppx =
field_b "bisect_ppx"
~check:(Dune_lang.Syntax.since bisect_ppx_syntax (1, 0))
and+ version = Dune_lang.Syntax.get_exn Stanza.syntax in
let foreign_stubs =
foreign_stubs
Expand Down Expand Up @@ -468,6 +503,7 @@ module Buildable = struct
; flags
; js_of_ocaml
; allow_overlapping_dependencies
; bisect_ppx
}

let has_foreign t =
Expand All @@ -481,6 +517,12 @@ module Buildable = struct
Per_module.get t.preprocess dummy_name
else
Preprocess.No_preprocessing

let preprocess t ~(lib_config: Lib_config.t) =
if t.bisect_ppx && lib_config.bisect_enabled then
Preprocess_map.add_bisect t.preprocess
else
t.preprocess
end

module Public_lib = struct
Expand Down Expand Up @@ -1031,7 +1073,17 @@ module Library = struct
let synopsis = conf.synopsis in
let sub_systems = conf.sub_systems in
let ppx_runtime_deps = conf.ppx_runtime_libraries in
let pps = Preprocess_map.pps conf.buildable.preprocess in
let pps =
let pps_without_bisect = Preprocess_map.pps conf.buildable.preprocess in
if lib_config.bisect_enabled && conf.buildable.bisect_ppx then
let bisect_ppx =
let bisect_name = Lib_name.parse_string_exn (Loc.none, "bisect_ppx") in
(Loc.none, bisect_name)
in
bisect_ppx :: pps_without_bisect
else
pps_without_bisect
in
let virtual_deps = conf.virtual_deps in
let dune_version = Some conf.dune_version in
let implements = conf.implements in
Expand Down
4 changes: 4 additions & 0 deletions src/dune/dune_file.mli
Original file line number Diff line number Diff line change
Expand Up @@ -98,13 +98,17 @@ module Buildable : sig
; flags : Ocaml_flags.Spec.t
; js_of_ocaml : Js_of_ocaml.t
; allow_overlapping_dependencies : bool
; bisect_ppx : bool
}

(** Check if the buildable has any foreign stubs or archives. *)
val has_foreign : t -> bool

(** Preprocessing specification used by all modules or [No_preprocessing] *)
val single_preprocess : t -> Preprocess.t

(** Includes bisect_ppx if specified by [lib_config] *)
val preprocess : t -> lib_config:Lib_config.t -> Preprocess_map.t
end

module Public_lib : sig
Expand Down
16 changes: 11 additions & 5 deletions src/dune/exe_rules.ml
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,13 @@ let executables_rules ~sctx ~dir ~expander ~dir_contents ~scope ~compile_info
let ml_sources = Dir_contents.ocaml dir_contents in
Ml_sources.modules_of_executables ml_sources ~first_exe ~obj_dir
in
let ctx = SC.context sctx in
let preprocess =
Dune_file.Buildable.preprocess exes.buildable ~lib_config:ctx.lib_config
in
let pp =
Preprocessing.make sctx ~dir ~dep_kind:Required ~scope ~expander
~preprocess:exes.buildable.preprocess
~preprocess
~preprocessor_deps:exes.buildable.preprocessor_deps
~lint:exes.buildable.lint ~lib_name:None
in
Expand All @@ -44,7 +48,6 @@ let executables_rules ~sctx ~dir ~expander ~dir_contents ~scope ~compile_info
(Module_name.to_string mod_name)
])
in
let ctx = SC.context sctx in
let explicit_js_mode = Dune_project.explicit_js_mode (Scope.project scope) in
let linkages =
let module L = Dune_file.Executables.Link_mode in
Expand Down Expand Up @@ -170,11 +173,14 @@ let executables_rules ~sctx ~dir ~expander ~dir_contents ~scope ~compile_info
let rules ~sctx ~dir ~dir_contents ~scope ~expander
(exes : Dune_file.Executables.t) =
let dune_version = Scope.project scope |> Dune_project.dune_version in
let ctx = SC.context sctx in
let pps =
Dune_file.Preprocess_map.pps
(Dune_file.Buildable.preprocess exes.buildable ~lib_config:ctx.lib_config)
in
let compile_info =
Lib.DB.resolve_user_written_deps_for_exes (Scope.libs scope) exes.names
exes.buildable.libraries
~pps:(Dune_file.Preprocess_map.pps exes.buildable.preprocess)
~dune_version
exes.buildable.libraries ~pps ~dune_version
~allow_overlaps:exes.buildable.allow_overlapping_dependencies
~variants:exes.variants ~optional:exes.optional
~forbidden_libraries:exes.forbidden_libraries
Expand Down
Loading