From a2705edeb9745a43cff5360b16bd0bde0256d597 Mon Sep 17 00:00:00 2001 From: Jeremie Dimino Date: Tue, 8 May 2018 15:38:28 +0100 Subject: [PATCH 01/11] Add List.rev_filter_map{,i} --- src/stdune/list.ml | 30 ++++++++++++++++++++++++++++++ src/stdune/list.mli | 3 +++ 2 files changed, 33 insertions(+) diff --git a/src/stdune/list.ml b/src/stdune/list.ml index 06c7cb7df66..7566e583568 100644 --- a/src/stdune/list.ml +++ b/src/stdune/list.ml @@ -26,6 +26,36 @@ let filteri l ~f = in filteri l 0 +let rev_filter_map = + let rec loop acc l ~f = + match l with + | [] -> acc + | x :: l -> + let acc = + match f x with + | None -> acc + | Some y -> y :: acc + in + loop acc l ~f + in + fun l ~f -> + loop [] l ~f + +let rev_filter_mapi = + let rec loop acc l ~f i = + match l with + | [] -> acc + | x :: l -> + let acc = + match f i x with + | None -> acc + | Some y -> y :: acc + in + loop acc l ~f (i + 1) + in + fun l ~f -> + loop [] l ~f 0 + let concat_map l ~f = concat (map l ~f) let rev_partition_map = diff --git a/src/stdune/list.mli b/src/stdune/list.mli index 4cfe2dfdd5c..1f54de22847 100644 --- a/src/stdune/list.mli +++ b/src/stdune/list.mli @@ -8,6 +8,9 @@ val filter_map : 'a t -> f:('a -> 'b option) -> 'b t val filteri : 'a t -> f:(int -> 'a -> bool) -> 'a t +val rev_filter_map : 'a t -> f:( 'a -> 'b option) -> 'b t +val rev_filter_mapi : 'a t -> f:(int -> 'a -> 'b option) -> 'b t + val concat_map : 'a t -> f:('a -> 'b t) -> 'b t val partition_map : 'a t -> f:('a -> ('b, 'c) Either.t) -> 'b t * 'c t From 93adef0ea179d272f6ee5eb43be3260ca193d5b8 Mon Sep 17 00:00:00 2001 From: Jeremie Dimino Date: Tue, 8 May 2018 16:11:02 +0100 Subject: [PATCH 02/11] Add support for .dune-fs files --- CHANGES.md | 2 + src/build_system.ml | 4 +- src/file_tree.ml | 195 +++++++++++++++++++++++++++++++------------- src/file_tree.mli | 15 ++-- src/jbuild_load.ml | 6 +- 5 files changed, 155 insertions(+), 67 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index e803d29bea2..c2e977e4949 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -40,6 +40,8 @@ next - Fix a crash when using c files from another directory (#758, fixes #734, @diml) +- Add support for .dune-fs files (#750, @diml) + 1.0+beta20 (10/04/2018) ----------------------- diff --git a/src/build_system.ml b/src/build_system.ml index 3cecab2084c..1f434d9ddd6 100644 --- a/src/build_system.ml +++ b/src/build_system.ml @@ -263,7 +263,7 @@ module Alias0 = struct open Build.O let dep_rec_internal ~name ~dir ~ctx_dir = - File_tree.Dir.fold dir ~traverse_ignored_dirs:false ~init:(Build.return true) + File_tree.Dir.fold dir ~traverse_raw_data_dirs:false ~init:(Build.return true) ~f:(fun dir acc -> let path = Path.append ctx_dir (File_tree.Dir.path dir) in let fn = stamp_file (make ~dir:path name) in @@ -1168,7 +1168,7 @@ end let all_targets t = String.Map.iter t.contexts ~f:(fun ctx -> - File_tree.fold t.file_tree ~traverse_ignored_dirs:true ~init:() + File_tree.fold t.file_tree ~traverse_raw_data_dirs:true ~init:() ~f:(fun dir () -> load_dir t ~dir:(Path.append ctx.Context.build_dir (File_tree.Dir.path dir)))); diff --git a/src/file_tree.ml b/src/file_tree.ml index 067f163aa24..4023e1c7b55 100644 --- a/src/file_tree.ml +++ b/src/file_tree.ml @@ -1,21 +1,117 @@ open! Import +module Dune_fs = struct + open Sexp.Of_sexp + + type kind = + | File + | Dir + | Any + + type what = + | Standard + | Raw_data + | Ignore + + type entry = + { kind : kind + ; glob : Re.re + ; what : what + } + + type t = { rev_entries : entry list } [@@unboxed] + + let find = + let rec aux rev_entries fn ~is_directory = + match rev_entries with + | [] -> + if fn = "" || fn = "." || + (is_directory && (fn.[0] = '.' || fn.[0] = '_')) || + (fn.[0] = '.' && fn.[1] = '#') + then + Ignore + else + Standard + | e :: rest -> + if (match e.kind with + | File -> not is_directory + | Dir -> is_directory + | Any -> true) && + Re.execp e.glob fn then + e.what + else + aux rest fn ~is_directory + in + fun t fn ~is_directory -> + aux t.rev_entries fn ~is_directory + + let kind = + enum + [ "file" , File + ; "dir" , Dir + ; "_" , Any + ] + + let what = + enum + [ "standard" , Standard + ; "raw_data" , Raw_data + ; "ignore" , Ignore + ] + + let glob sexp = + let s = string sexp in + match Glob_lexer.parse_string s with + | Ok re -> Re.compile re + | Error (_pos, msg) -> of_sexp_errorf sexp "invalid glob: %s" msg + + let entry sexp = + let kind, glob, what = triple kind glob what sexp in + { kind; glob; what } + + let t sexps = + let rev_entries = List.rev_map sexps ~f:entry in + { rev_entries } + + let load path = + t (Io.Sexp.load path ~mode:Many) + + let load_jbuild_ignore path = + let rev_entries = + List.rev_filter_mapi (Io.lines_of_file path) ~f:(fun i fn -> + if Filename.dirname fn = Filename.current_dir_name then + Some { kind = Dir; glob = Re.compile (Re.str fn); what = Raw_data } + else begin + Loc.(warn (of_pos ( Path.to_string path + , i + 1, 0 + , String.length fn + )) + "subdirectory expression %s ignored" fn); + None + end) + in + { rev_entries } + + let default = { rev_entries = [] } +end + module Dir = struct type t = { path : Path.t - ; ignored : bool + ; raw_data : bool ; contents : contents Lazy.t } and contents = { files : String.Set.t ; sub_dirs : t String.Map.t + ; dune_fs : Dune_fs.t } let contents t = Lazy.force t.contents let path t = t.path - let ignored t = t.ignored + let raw_data t = t.raw_data let files t = (contents t).files let sub_dirs t = (contents t).sub_dirs @@ -31,22 +127,23 @@ module Dir = struct String.Map.foldi (sub_dirs t) ~init:Path.Set.empty ~f:(fun s _ acc -> Path.Set.add acc (Path.relative t.path s)) - let rec fold t ~traverse_ignored_dirs ~init:acc ~f = - if not traverse_ignored_dirs && t.ignored then + let rec fold t ~traverse_raw_data_dirs ~init:acc ~f = + if not traverse_raw_data_dirs && t.raw_data then acc else let acc = f t acc in String.Map.fold (sub_dirs t) ~init:acc ~f:(fun t acc -> - fold t ~traverse_ignored_dirs ~init:acc ~f) + fold t ~traverse_raw_data_dirs ~init:acc ~f) let dune_file t = - let (lazy { files; _ }) = t.contents in - if String.Set.mem files "dune" then - Some (Path.relative t.path "dune") - else if String.Set.mem files "jbuild" then - Some (Path.relative t.path "jbuild") - else - None + let (lazy { files; dune_fs; _ }) = t.contents in + List.find_map ["dune"; "jbuild"] ~f:(fun fn -> + if String.Set.mem files fn then + match Dune_fs.find dune_fs fn ~is_directory:false with + | Standard -> Some (Path.relative t.path fn) + | _ -> None + else + None) end type t = @@ -56,71 +153,59 @@ type t = let root t = t.root -let ignore_file fn ~is_directory = - fn = "" || fn = "." || - (is_directory && (fn.[0] = '.' || fn.[0] = '_')) || - (fn.[0] = '.' && fn.[1] = '#') - let load ?(extra_ignored_subtrees=Path.Set.empty) path = - let rec walk path ~ignored : Dir.t = + let rec walk path ~raw_data : Dir.t = let contents = lazy ( let files, sub_dirs = Path.readdir path - |> List.filter_partition_map ~f:(fun fn -> + |> List.partition_map ~f:(fun fn -> let path = Path.relative path fn in - let is_directory = Path.is_directory path in - if ignore_file fn ~is_directory then - Skip - else if is_directory then + if Path.is_directory path then Right (fn, path) else Left fn) in let files = String.Set.of_list files in - let ignored_sub_dirs = - if not ignored && String.Set.mem files "jbuild-ignore" then - let ignore_file = Path.relative path "jbuild-ignore" in - let files = - Io.lines_of_file ignore_file - in - let remove_subdirs index fn = - if Filename.dirname fn = Filename.current_dir_name then - true - else begin - Loc.(warn (of_pos ( Path.to_string ignore_file - , index + 1, 0, String.length fn)) - "subdirectory expression %s ignored" fn); - false - end - in - String.Set.of_list (List.filteri ~f:remove_subdirs files) + let dune_fs = + if String.Set.mem files ".dune-fs" then + Dune_fs.load (Path.relative path ".dune-fs") + else if String.Set.mem files "jbuild-ignore" then + Dune_fs.load_jbuild_ignore (Path.relative path "jbuild-ignore") else - String.Set.empty + Dune_fs.default + in + let files = + String.Set.filter files ~f:(fun fn -> + match Dune_fs.find dune_fs fn ~is_directory:false with + | Ignore -> false + | Standard | Raw_data -> true) in let sub_dirs = - List.map sub_dirs ~f:(fun (fn, path) -> - let ignored = - ignored - || String.Set.mem ignored_sub_dirs fn - || Path.Set.mem extra_ignored_subtrees path - in - (fn, walk path ~ignored)) - |> String.Map.of_list_exn + List.fold_left sub_dirs ~init:String.Map.empty ~f:(fun acc (fn, path) -> + match + match Dune_fs.find dune_fs fn ~is_directory:true with + | Ignore -> None + | Standard -> Some (raw_data || + Path.Set.mem extra_ignored_subtrees path) + | Raw_data -> Some true + with + | None -> acc + | Some raw_data -> String.Map.add acc fn (walk path ~raw_data)) in - { Dir. files; sub_dirs }) + { Dir. files; sub_dirs; dune_fs }) in { path ; contents - ; ignored + ; raw_data } in - let root = walk path ~ignored:false in + let root = walk path ~raw_data:false in let dirs = Hashtbl.create 1024 in Hashtbl.add dirs Path.root root; { root; dirs } -let fold t ~traverse_ignored_dirs ~init ~f = - Dir.fold t.root ~traverse_ignored_dirs ~init ~f +let fold t ~traverse_raw_data_dirs ~init ~f = + Dir.fold t.root ~traverse_raw_data_dirs ~init ~f let rec find_dir t path = if not (Path.is_local path) then @@ -166,7 +251,7 @@ let files_recursively_in t ?(prefix_with=Path.root) path = match find_dir t path with | None -> Path.Set.empty | Some dir -> - Dir.fold dir ~init:Path.Set.empty ~traverse_ignored_dirs:true + Dir.fold dir ~init:Path.Set.empty ~traverse_raw_data_dirs:true ~f:(fun dir acc -> let path = Path.append prefix_with (Dir.path dir) in String.Set.fold (Dir.files dir) ~init:acc ~f:(fun fn acc -> diff --git a/src/file_tree.mli b/src/file_tree.mli index be098fa9ea0..8405e434520 100644 --- a/src/file_tree.mli +++ b/src/file_tree.mli @@ -12,13 +12,14 @@ module Dir : sig val sub_dir_paths : t -> Path.Set.t val sub_dir_names : t -> String.Set.t - (** Whether this directory is ignored by a [jbuild-ignore] file in - one of its ancestor directories. *) - val ignored : t -> bool + (** Whether this directory contains raw data, as configured by a + [.dune-fs] or [jbuild-ignore] file in one of its ancestor + directories. *) + val raw_data : t -> bool val fold : t - -> traverse_ignored_dirs:bool + -> traverse_raw_data_dirs:bool -> init:'a -> f:(t -> 'a -> 'a) -> 'a @@ -35,12 +36,12 @@ type t val load : ?extra_ignored_subtrees:Path.Set.t -> Path.t -> t -(** Passing [~traverse_ignored_dirs:true] to this functions causes the - whole source tree to be deeply scanned, including ignored +(** Passing [~traverse_raw_data_dirs:true] to this functions causes the + whole source tree to be deeply scanned, including raw_data directories. *) val fold : t - -> traverse_ignored_dirs:bool + -> traverse_raw_data_dirs:bool -> init:'a -> f:(Dir.t -> 'a -> 'a) -> 'a diff --git a/src/jbuild_load.ml b/src/jbuild_load.ml index a387816ed16..3f18889a2dd 100644 --- a/src/jbuild_load.ml +++ b/src/jbuild_load.ml @@ -219,7 +219,7 @@ let load ~dir ~scope ~ignore_promoted_rules ~file = let load ?extra_ignored_subtrees ?(ignore_promoted_rules=false) () = let ftree = File_tree.load Path.root ?extra_ignored_subtrees in let packages = - File_tree.fold ftree ~traverse_ignored_dirs:false ~init:[] ~f:(fun dir pkgs -> + File_tree.fold ftree ~traverse_raw_data_dirs:false ~init:[] ~f:(fun dir pkgs -> let path = File_tree.Dir.path dir in let files = File_tree.Dir.files dir in String.Set.fold files ~init:pkgs ~f:(fun fn acc -> @@ -259,7 +259,7 @@ let load ?extra_ignored_subtrees ?(ignore_promoted_rules=false) () = in let projects = - File_tree.fold ftree ~traverse_ignored_dirs:false ~init:[] + File_tree.fold ftree ~traverse_raw_data_dirs:false ~init:[] ~f:(fun dir acc -> let path = File_tree.Dir.path dir in let files = File_tree.Dir.files dir in @@ -294,7 +294,7 @@ let load ?extra_ignored_subtrees ?(ignore_promoted_rules=false) () = Path.Map.add scopes Path.root Scope_info.anonymous in let rec walk dir jbuilds scope = - if File_tree.Dir.ignored dir then + if File_tree.Dir.raw_data dir then jbuilds else begin let path = File_tree.Dir.path dir in From 5bee48fc5059c2546200fb4f2e0058d42670c446 Mon Sep 17 00:00:00 2001 From: Jeremie Dimino Date: Tue, 8 May 2018 16:13:03 +0100 Subject: [PATCH 03/11] Rename jbuild-ignore files --- example/.dune-fs | 1 + example/jbuild-ignore | 1 - test/blackbox-tests/.dune-fs | 1 + test/blackbox-tests/jbuild-ignore | 1 - 4 files changed, 2 insertions(+), 2 deletions(-) create mode 100644 example/.dune-fs delete mode 100644 example/jbuild-ignore create mode 100644 test/blackbox-tests/.dune-fs delete mode 100644 test/blackbox-tests/jbuild-ignore diff --git a/example/.dune-fs b/example/.dune-fs new file mode 100644 index 00000000000..1bd01dfe0d7 --- /dev/null +++ b/example/.dune-fs @@ -0,0 +1 @@ +(dir sample-projects raw_data) diff --git a/example/jbuild-ignore b/example/jbuild-ignore deleted file mode 100644 index 9741c984949..00000000000 --- a/example/jbuild-ignore +++ /dev/null @@ -1 +0,0 @@ -sample-projects diff --git a/test/blackbox-tests/.dune-fs b/test/blackbox-tests/.dune-fs new file mode 100644 index 00000000000..20f09d37a4b --- /dev/null +++ b/test/blackbox-tests/.dune-fs @@ -0,0 +1 @@ +(dir test-cases raw_data) diff --git a/test/blackbox-tests/jbuild-ignore b/test/blackbox-tests/jbuild-ignore deleted file mode 100644 index 1007655629a..00000000000 --- a/test/blackbox-tests/jbuild-ignore +++ /dev/null @@ -1 +0,0 @@ -test-cases From 2823525c6fae02dcccb885772e1883c6e288fab3 Mon Sep 17 00:00:00 2001 From: Jeremie Dimino Date: Tue, 8 May 2018 16:15:59 +0100 Subject: [PATCH 04/11] Fix bootstrap --- src/glob_lexer.boot.ml | 3 +++ vendor/boot/re.ml | 1 + 2 files changed, 4 insertions(+) diff --git a/src/glob_lexer.boot.ml b/src/glob_lexer.boot.ml index d7d30df7ef0..e88b931e67d 100644 --- a/src/glob_lexer.boot.ml +++ b/src/glob_lexer.boot.ml @@ -2,3 +2,6 @@ ocaml-re. This speeds up the bootstrap. *) let parse_string _ = failwith "globs are not available during bootstrap" + +(* To force the ordering during bootstrap *) +let _ = Re.compile diff --git a/vendor/boot/re.ml b/vendor/boot/re.ml index 34fce92c04a..898b89823d9 100644 --- a/vendor/boot/re.ml +++ b/vendor/boot/re.ml @@ -2,3 +2,4 @@ type t = unit type re = unit let compile () = () let execp _ _ = false +let str _ = () From 6fdb265a57bcfd327ac06bdd50136b898968f0a3 Mon Sep 17 00:00:00 2001 From: Jeremie Dimino Date: Tue, 8 May 2018 16:28:20 +0100 Subject: [PATCH 05/11] Document .dune-fs files --- doc/project-layout-specification.rst | 57 ++++++++++++++++++++++------ 1 file changed, 45 insertions(+), 12 deletions(-) diff --git a/doc/project-layout-specification.rst b/doc/project-layout-specification.rst index b89ee580e21..1367eabc86d 100644 --- a/doc/project-layout-specification.rst +++ b/doc/project-layout-specification.rst @@ -221,20 +221,53 @@ Note that this includes files present in the source tree as well as generated files. So for instance a changelog generated by a user rule will be automatically installed as well. -jbuild-ignore -============= +.dune-fs +======== -By default Jbuilder traverses the whole source tree, ignoring the -following files and directories: +``.dune-fs`` files allows to control how Dune interprets the file +system. They can be used to make Dune completely ignore certain files +or directories or to make a sub-tree appear as plain raw data. -- any file that start with ``.#`` -- any directory that start with either ``.`` or ``_`` +They consist of a list of entries of the form: -To ignore a subtree, simply write a ``jbuild-ignore`` file in the -parent directory containing the name of the sub-directories to ignore. +.. code:: scheme + + ( ) + +Where ```` is one of ``file``, ``directory`` or ``_`` and +indicate whether the entry matches files, directories or both. See +:ref:`glob ` ford details about what globs are +available. ```` gives the status of files and/or directories +matched by this entry and can be one of: + +- ``standard`` +- ``raw_data`` meaning that Dune will not try to interpret ``Dune`` + or other special files for this file or sub-tree. This also prevents + Dune from eagerly scanning a sub-tree +- ``ignore`` meaning that Dune will completely ignore this file or + sub-tree. It is the same as removing it before running Dune + +The status of a given file or sub-directory is given by the last entry +that matches. The following default is assumed in any directory that +doesn't have a ``.dune-fs`` file: + +.. code:: scheme + + (dir [._]* ignore) + (_ .#* ignore) + +So for instance ``.git`` directories are completely ignored. If you +wanted to write an action that would read files inside the ``.git`` +directory, you could override this default by adding the following +``.dune-fs`` file: + +.. code:: scheme + + (dir .git raw_data) -So for instance, if you write ``foo`` in ``src/jbuild-ignore``, then -``src/foo`` won't be traversed and any ``jbuild`` file it contains will -be ignored. +jbuild-ignore (deprecated) +========================== -``jbuild-ignore`` files contain a list of directory names, one per line. +``jbuild-ignore`` files are deprecated. Each line of a +``jbuild-ignore`` file is interpreted in the same way as a ``(dir + raw_data)`` entry in a ``.dune-fs`` file. From 1af3bc9aa6965a078b9f98b4fca1fb458fb1d877 Mon Sep 17 00:00:00 2001 From: Jeremie Dimino Date: Thu, 10 May 2018 12:29:10 +0100 Subject: [PATCH 06/11] Replace .dune-fs by fs stanza --- src/build_system.ml | 2 +- src/file_tree.ml | 111 ++++++++++++++++++++++++++++++++++---------- src/file_tree.mli | 12 ++++- src/jbuild_load.ml | 52 +++------------------ 4 files changed, 105 insertions(+), 72 deletions(-) diff --git a/src/build_system.ml b/src/build_system.ml index 1f434d9ddd6..d05549c9765 100644 --- a/src/build_system.ml +++ b/src/build_system.ml @@ -134,7 +134,7 @@ let rule_loc ~file_tree ~loc ~dir = Option.bind (File_tree.find_dir file_tree dir) ~f:File_tree.Dir.dune_file with - | Some file -> file + | Some file -> File_tree.Dune_file.path file | None -> Path.relative dir "_unknown_" in Loc.in_file (Path.to_string file) diff --git a/src/file_tree.ml b/src/file_tree.ml index 4023e1c7b55..3a2602a603d 100644 --- a/src/file_tree.ml +++ b/src/file_tree.ml @@ -1,5 +1,47 @@ open! Import +module Dune_file = struct + type t = + | Sexps of Path.t * Sexp.Ast.t list + | Ocaml_script of Path.t + + let path = function + | Sexps (p, _) -> p + | Ocaml_script p -> p + + let ocaml_script_prefix = "(* -*- tuareg -*- *)" + let ocaml_script_prefix_len = String.length ocaml_script_prefix + + let load file = + Io.with_file_in file ~f:(fun ic -> + let open Sexp in + let state = Parser.create ~fname:(Path.to_string file) ~mode:Many in + let buf = Bytes.create Io.buf_len in + let rec loop stack = + match input ic buf 0 Io.buf_len with + | 0 -> Parser.feed_eoi state stack + | n -> loop (Parser.feed_subbytes state buf ~pos:0 ~len:n stack) + in + let rec loop0 stack i = + match input ic buf i (Io.buf_len - i) with + | 0 -> + let stack = Parser.feed_subbytes state buf ~pos:0 ~len:i stack in + Sexps (file, Parser.feed_eoi state stack) + | n -> + let i = i + n in + if i < ocaml_script_prefix_len then + loop0 stack i + else if Bytes.sub_string buf 0 ocaml_script_prefix_len + [@warning "-6"] + = ocaml_script_prefix then + Ocaml_script file + else + let stack = Parser.feed_subbytes state buf ~pos:0 ~len:i stack in + Sexps (file, loop stack) + in + loop0 Parser.Stack.empty 0) +end + module Dune_fs = struct open Sexp.Of_sexp @@ -73,9 +115,6 @@ module Dune_fs = struct let rev_entries = List.rev_map sexps ~f:entry in { rev_entries } - let load path = - t (Io.Sexp.load path ~mode:Many) - let load_jbuild_ignore path = let rev_entries = List.rev_filter_mapi (Io.lines_of_file path) ~f:(fun i fn -> @@ -103,9 +142,10 @@ module Dir = struct } and contents = - { files : String.Set.t - ; sub_dirs : t String.Map.t - ; dune_fs : Dune_fs.t + { files : String.Set.t + ; sub_dirs : t String.Map.t + ; dune_fs : Dune_fs.t + ; dune_file : Dune_file.t option } let contents t = Lazy.force t.contents @@ -113,8 +153,9 @@ module Dir = struct let path t = t.path let raw_data t = t.raw_data - let files t = (contents t).files - let sub_dirs t = (contents t).sub_dirs + let files t = (contents t).files + let sub_dirs t = (contents t).sub_dirs + let dune_file t = (contents t).dune_file let file_paths t = Path.Set.of_string_set (files t) ~f:(Path.relative t.path) @@ -134,16 +175,6 @@ module Dir = struct let acc = f t acc in String.Map.fold (sub_dirs t) ~init:acc ~f:(fun t acc -> fold t ~traverse_raw_data_dirs ~init:acc ~f) - - let dune_file t = - let (lazy { files; dune_fs; _ }) = t.contents in - List.find_map ["dune"; "jbuild"] ~f:(fun fn -> - if String.Set.mem files fn then - match Dune_fs.find dune_fs fn ~is_directory:false with - | Standard -> Some (Path.relative t.path fn) - | _ -> None - else - None) end type t = @@ -166,13 +197,45 @@ let load ?(extra_ignored_subtrees=Path.Set.empty) path = Left fn) in let files = String.Set.of_list files in + let dune_file = + match List.filter ["dune"; "jbuild"] ~f:(String.Set.mem files) with + | [] -> None + | [fn] -> Some (Dune_file.load (Path.relative path fn)) + | _ -> + die "Directory %s has both a 'dune' and 'jbuild' file.\n\ + This is not allowed" + (Path.to_string_maybe_quoted path) + in + let dune_file, dune_fs = + match dune_file with + | None | Some (Ocaml_script _) -> (dune_file, None) + | Some (Sexps (file, l)) -> + let fs_stanzas, other = + List.partition_map l ~f:(function + | List (loc, Atom (_, A "fs") :: sexps) -> Left (loc, sexps) + | x -> Right x) + in + match fs_stanzas with + | [] -> (dune_file, None) + | _ :: (loc, _) :: _ -> + Loc.fail loc "Too many fs stanzas." + | [(loc, sexps)] -> + (Some (Sexps (file, other)), Some (loc, Dune_fs.t sexps)) + in let dune_fs = - if String.Set.mem files ".dune-fs" then - Dune_fs.load (Path.relative path ".dune-fs") - else if String.Set.mem files "jbuild-ignore" then - Dune_fs.load_jbuild_ignore (Path.relative path "jbuild-ignore") + if String.Set.mem files "jbuild-ignore" then + let file = Path.relative path "jbuild-ignore" in + match dune_fs with + | None -> + Dune_fs.load_jbuild_ignore file + | Some (loc, _) -> + Loc.fail loc + "It is not allowed to have both a fs stanza and a \ + jbuild-ignore file in the same directory." else - Dune_fs.default + match dune_fs with + | None -> Dune_fs.default + | Some (_, x) -> x in let files = String.Set.filter files ~f:(fun fn -> @@ -192,7 +255,7 @@ let load ?(extra_ignored_subtrees=Path.Set.empty) path = | None -> acc | Some raw_data -> String.Map.add acc fn (walk path ~raw_data)) in - { Dir. files; sub_dirs; dune_fs }) + { Dir. files; sub_dirs; dune_fs; dune_file }) in { path ; contents diff --git a/src/file_tree.mli b/src/file_tree.mli index 8405e434520..4206d1590e8 100644 --- a/src/file_tree.mli +++ b/src/file_tree.mli @@ -2,6 +2,14 @@ open! Import +module Dune_file : sig + type t = + | Sexps of Path.t * Sexp.Ast.t list + | Ocaml_script of Path.t + + val path : t -> Path.t +end + module Dir : sig type t @@ -24,8 +32,8 @@ module Dir : sig -> f:(t -> 'a -> 'a) -> 'a - (** Return the dune (or jbuild) file in this directory *) - val dune_file : t -> Path.t option + (** Return the contents of the dune (or jbuild) file in this directory *) + val dune_file : t -> Dune_file.t option end (** A [t] value represent a view of the source tree. It is lazily diff --git a/src/jbuild_load.ml b/src/jbuild_load.ml index 3f18889a2dd..24035cfa0c3 100644 --- a/src/jbuild_load.ml +++ b/src/jbuild_load.ml @@ -168,52 +168,14 @@ type conf = ; scopes : Scope_info.t list } -module Sexp_io = struct - open Sexp - - let ocaml_script_prefix = "(* -*- tuareg -*- *)" - let ocaml_script_prefix_len = String.length ocaml_script_prefix - - type sexps_or_ocaml_script = - | Sexps of Ast.t list - | Ocaml_script - - let load_many_or_ocaml_script fname = - Io.with_file_in fname ~f:(fun ic -> - let state = Parser.create ~fname:(Path.to_string fname) ~mode:Many in - let buf = Bytes.create Io.buf_len in - let rec loop stack = - match input ic buf 0 Io.buf_len with - | 0 -> Parser.feed_eoi state stack - | n -> loop (Parser.feed_subbytes state buf ~pos:0 ~len:n stack) - in - let rec loop0 stack i = - match input ic buf i (Io.buf_len - i) with - | 0 -> - let stack = Parser.feed_subbytes state buf ~pos:0 ~len:i stack in - Sexps (Parser.feed_eoi state stack) - | n -> - let i = i + n in - if i < ocaml_script_prefix_len then - loop0 stack i - else if Bytes.sub_string buf 0 ocaml_script_prefix_len - [@warning "-6"] - = ocaml_script_prefix then - Ocaml_script - else - let stack = Parser.feed_subbytes state buf ~pos:0 ~len:i stack in - Sexps (loop stack) - in - loop0 Parser.Stack.empty 0) -end - -let load ~dir ~scope ~ignore_promoted_rules ~file = - match Sexp_io.load_many_or_ocaml_script file with - | Sexps sexps -> +let interpret ~dir ~scope ~ignore_promoted_rules + ~(dune_file:File_tree.Dune_file.t) = + match dune_file with + | Sexps (file, sexps) -> Jbuilds.Literal (dir, scope, Stanzas.parse scope sexps ~file |> filter_stanzas ~ignore_promoted_rules) - | Ocaml_script -> + | Ocaml_script file -> Script { dir; scope; file } let load ?extra_ignored_subtrees ?(ignore_promoted_rules=false) () = @@ -303,9 +265,9 @@ let load ?extra_ignored_subtrees ?(ignore_promoted_rules=false) () = let jbuilds = match File_tree.Dir.dune_file dir with | None -> jbuilds - | Some file -> + | Some dune_file -> let jbuild = - load ~dir:path ~scope ~ignore_promoted_rules ~file + interpret ~dir:path ~scope ~ignore_promoted_rules ~dune_file in jbuild :: jbuilds in From 0d299d941149c0f6fd7131a3033b49422e0f2752 Mon Sep 17 00:00:00 2001 From: Jeremie Dimino Date: Thu, 10 May 2018 12:30:10 +0100 Subject: [PATCH 07/11] Use fs stanzas --- example/.dune-fs | 1 - example/dune | 3 +++ test/blackbox-tests/.dune-fs | 1 - test/blackbox-tests/dune | 3 +++ 4 files changed, 6 insertions(+), 2 deletions(-) delete mode 100644 example/.dune-fs delete mode 100644 test/blackbox-tests/.dune-fs diff --git a/example/.dune-fs b/example/.dune-fs deleted file mode 100644 index 1bd01dfe0d7..00000000000 --- a/example/.dune-fs +++ /dev/null @@ -1 +0,0 @@ -(dir sample-projects raw_data) diff --git a/example/dune b/example/dune index a96fdcae683..51ef72196e8 100644 --- a/example/dune +++ b/example/dune @@ -1,5 +1,8 @@ (jbuild_version 1) +(fs + (dir sample-projects raw_data)) + (alias ((name runtest) (deps ((package dune) diff --git a/test/blackbox-tests/.dune-fs b/test/blackbox-tests/.dune-fs deleted file mode 100644 index 20f09d37a4b..00000000000 --- a/test/blackbox-tests/.dune-fs +++ /dev/null @@ -1 +0,0 @@ -(dir test-cases raw_data) diff --git a/test/blackbox-tests/dune b/test/blackbox-tests/dune index a8254a66069..b519e7465d8 100644 --- a/test/blackbox-tests/dune +++ b/test/blackbox-tests/dune @@ -1,5 +1,8 @@ (jbuild_version 1) +(fs + (dir test-cases raw_data)) + (library ((name platform) (modules (platform)) From bf7243854526bdb0dcee1f3358a1a2e80168a50b Mon Sep 17 00:00:00 2001 From: Jeremie Dimino Date: Thu, 10 May 2018 12:35:26 +0100 Subject: [PATCH 08/11] optim --- src/file_tree.ml | 25 ++++++++++++++++--------- src/file_tree.mli | 12 +++++++++++- src/jbuild_load.ml | 12 ++++++++---- 3 files changed, 35 insertions(+), 14 deletions(-) diff --git a/src/file_tree.ml b/src/file_tree.ml index 3a2602a603d..41c19dfb649 100644 --- a/src/file_tree.ml +++ b/src/file_tree.ml @@ -1,14 +1,21 @@ open! Import module Dune_file = struct + module Plain = struct + type t = + { path : Path.t + ; mutable sexps : Sexp.Ast.t list + } + end + type t = - | Sexps of Path.t * Sexp.Ast.t list + | Plain of Plain.t | Ocaml_script of Path.t let path = function - | Sexps (p, _) -> p - | Ocaml_script p -> p - + | Plain x -> x.path + | Ocaml_script p -> p + let ocaml_script_prefix = "(* -*- tuareg -*- *)" let ocaml_script_prefix_len = String.length ocaml_script_prefix @@ -26,7 +33,7 @@ module Dune_file = struct match input ic buf i (Io.buf_len - i) with | 0 -> let stack = Parser.feed_subbytes state buf ~pos:0 ~len:i stack in - Sexps (file, Parser.feed_eoi state stack) + Plain { path = file; sexps = Parser.feed_eoi state stack } | n -> let i = i + n in if i < ocaml_script_prefix_len then @@ -37,7 +44,7 @@ module Dune_file = struct Ocaml_script file else let stack = Parser.feed_subbytes state buf ~pos:0 ~len:i stack in - Sexps (file, loop stack) + Plain { path = file; sexps = loop stack } in loop0 Parser.Stack.empty 0) end @@ -209,9 +216,9 @@ let load ?(extra_ignored_subtrees=Path.Set.empty) path = let dune_file, dune_fs = match dune_file with | None | Some (Ocaml_script _) -> (dune_file, None) - | Some (Sexps (file, l)) -> + | Some (Plain { path; sexps }) -> let fs_stanzas, other = - List.partition_map l ~f:(function + List.partition_map sexps ~f:(function | List (loc, Atom (_, A "fs") :: sexps) -> Left (loc, sexps) | x -> Right x) in @@ -220,7 +227,7 @@ let load ?(extra_ignored_subtrees=Path.Set.empty) path = | _ :: (loc, _) :: _ -> Loc.fail loc "Too many fs stanzas." | [(loc, sexps)] -> - (Some (Sexps (file, other)), Some (loc, Dune_fs.t sexps)) + (Some (Plain { path; sexps = other }), Some (loc, Dune_fs.t sexps)) in let dune_fs = if String.Set.mem files "jbuild-ignore" then diff --git a/src/file_tree.mli b/src/file_tree.mli index 4206d1590e8..07266e6f596 100644 --- a/src/file_tree.mli +++ b/src/file_tree.mli @@ -3,8 +3,18 @@ open! Import module Dune_file : sig + module Plain : sig + (** [sexps] is mutable as we get of the S-expressions once they + have been parsed, in order to release the memory as soon as we + don't need them. *) + type t = + { path : Path.t + ; mutable sexps : Sexp.Ast.t list + } + end + type t = - | Sexps of Path.t * Sexp.Ast.t list + | Plain of Plain.t | Ocaml_script of Path.t val path : t -> Path.t diff --git a/src/jbuild_load.ml b/src/jbuild_load.ml index 24035cfa0c3..92846e12938 100644 --- a/src/jbuild_load.ml +++ b/src/jbuild_load.ml @@ -171,10 +171,14 @@ type conf = let interpret ~dir ~scope ~ignore_promoted_rules ~(dune_file:File_tree.Dune_file.t) = match dune_file with - | Sexps (file, sexps) -> - Jbuilds.Literal (dir, scope, - Stanzas.parse scope sexps ~file - |> filter_stanzas ~ignore_promoted_rules) + | Plain p -> + let jbuild = + Jbuilds.Literal (dir, scope, + Stanzas.parse scope p.sexps ~file:p.path + |> filter_stanzas ~ignore_promoted_rules) + in + p.sexps <- []; + jbuild | Ocaml_script file -> Script { dir; scope; file } From 304e981ce50bc17ca3e6b188003597c07213aa78 Mon Sep 17 00:00:00 2001 From: Jeremie Dimino Date: Thu, 10 May 2018 15:13:27 +0100 Subject: [PATCH 09/11] typo --- src/file_tree.mli | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/file_tree.mli b/src/file_tree.mli index 07266e6f596..09dc663e8e3 100644 --- a/src/file_tree.mli +++ b/src/file_tree.mli @@ -4,9 +4,9 @@ open! Import module Dune_file : sig module Plain : sig - (** [sexps] is mutable as we get of the S-expressions once they - have been parsed, in order to release the memory as soon as we - don't need them. *) + (** [sexps] is mutable as we get rid of the S-expressions once + they have been parsed, in order to release the memory as soon + as we don't need them. *) type t = { path : Path.t ; mutable sexps : Sexp.Ast.t list From 2787c95aca5ad649c5ee840ed3bee419e441508c Mon Sep 17 00:00:00 2001 From: Jeremie Dimino Date: Thu, 10 May 2018 15:25:20 +0100 Subject: [PATCH 10/11] Add a test for fs stanza --- test/blackbox-tests/dune.inc | 10 ++++++++++ test/blackbox-tests/test-cases/fs-stanza/data/dune | 1 + test/blackbox-tests/test-cases/fs-stanza/dune | 3 +++ .../blackbox-tests/test-cases/fs-stanza/ignored-dir/x | 0 test/blackbox-tests/test-cases/fs-stanza/ignored-file | 0 .../test-cases/fs-stanza/old-style/data/dune | 1 + .../test-cases/fs-stanza/old-style/data/jbuild | 1 + .../test-cases/fs-stanza/old-style/jbuild-ignore | 1 + test/blackbox-tests/test-cases/fs-stanza/run.t | 11 +++++++++++ 9 files changed, 28 insertions(+) create mode 100644 test/blackbox-tests/test-cases/fs-stanza/data/dune create mode 100644 test/blackbox-tests/test-cases/fs-stanza/dune create mode 100644 test/blackbox-tests/test-cases/fs-stanza/ignored-dir/x create mode 100644 test/blackbox-tests/test-cases/fs-stanza/ignored-file create mode 100644 test/blackbox-tests/test-cases/fs-stanza/old-style/data/dune create mode 100644 test/blackbox-tests/test-cases/fs-stanza/old-style/data/jbuild create mode 100644 test/blackbox-tests/test-cases/fs-stanza/old-style/jbuild-ignore create mode 100644 test/blackbox-tests/test-cases/fs-stanza/run.t diff --git a/test/blackbox-tests/dune.inc b/test/blackbox-tests/dune.inc index e9eeb6cb82d..089870dd242 100644 --- a/test/blackbox-tests/dune.inc +++ b/test/blackbox-tests/dune.inc @@ -106,6 +106,14 @@ test-cases/force-test (progn (run ${exe:cram.exe} -test run.t) (diff? run.t run.t.corrected)))))) +(alias + ((name fs-stanza) + (deps ((package dune) (files_recursively_in test-cases/fs-stanza))) + (action + (chdir + test-cases/fs-stanza + (progn (run ${exe:cram.exe} -test run.t) (diff? run.t run.t.corrected)))))) + (alias ((name gen-opam-install-file) (deps @@ -490,6 +498,7 @@ (alias exec-cmd) (alias findlib) (alias force-test) + (alias fs-stanza) (alias gen-opam-install-file) (alias github20) (alias github24) @@ -547,6 +556,7 @@ (alias exec-cmd) (alias findlib) (alias force-test) + (alias fs-stanza) (alias gen-opam-install-file) (alias github20) (alias github24) diff --git a/test/blackbox-tests/test-cases/fs-stanza/data/dune b/test/blackbox-tests/test-cases/fs-stanza/data/dune new file mode 100644 index 00000000000..9ce7649da02 --- /dev/null +++ b/test/blackbox-tests/test-cases/fs-stanza/data/dune @@ -0,0 +1 @@ +garbage diff --git a/test/blackbox-tests/test-cases/fs-stanza/dune b/test/blackbox-tests/test-cases/fs-stanza/dune new file mode 100644 index 00000000000..11feb541e60 --- /dev/null +++ b/test/blackbox-tests/test-cases/fs-stanza/dune @@ -0,0 +1,3 @@ +(fs + (dir data raw_data) + (_ ignored-* ignore)) diff --git a/test/blackbox-tests/test-cases/fs-stanza/ignored-dir/x b/test/blackbox-tests/test-cases/fs-stanza/ignored-dir/x new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/blackbox-tests/test-cases/fs-stanza/ignored-file b/test/blackbox-tests/test-cases/fs-stanza/ignored-file new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/blackbox-tests/test-cases/fs-stanza/old-style/data/dune b/test/blackbox-tests/test-cases/fs-stanza/old-style/data/dune new file mode 100644 index 00000000000..d58adb0f78a --- /dev/null +++ b/test/blackbox-tests/test-cases/fs-stanza/old-style/data/dune @@ -0,0 +1 @@ +garbage \ No newline at end of file diff --git a/test/blackbox-tests/test-cases/fs-stanza/old-style/data/jbuild b/test/blackbox-tests/test-cases/fs-stanza/old-style/data/jbuild new file mode 100644 index 00000000000..9ce7649da02 --- /dev/null +++ b/test/blackbox-tests/test-cases/fs-stanza/old-style/data/jbuild @@ -0,0 +1 @@ +garbage diff --git a/test/blackbox-tests/test-cases/fs-stanza/old-style/jbuild-ignore b/test/blackbox-tests/test-cases/fs-stanza/old-style/jbuild-ignore new file mode 100644 index 00000000000..6320cd248dd --- /dev/null +++ b/test/blackbox-tests/test-cases/fs-stanza/old-style/jbuild-ignore @@ -0,0 +1 @@ +data \ No newline at end of file diff --git a/test/blackbox-tests/test-cases/fs-stanza/run.t b/test/blackbox-tests/test-cases/fs-stanza/run.t new file mode 100644 index 00000000000..0418657717f --- /dev/null +++ b/test/blackbox-tests/test-cases/fs-stanza/run.t @@ -0,0 +1,11 @@ + $ jbuilder build data/dune + $ jbuilder build old-style/data/dune + +The follow command must fail due to the fs settings: + + $ jbuilder build ignored-file + Don't know how to build ignored-file + [1] + $ jbuilder build ignored-dir/x + Don't know how to build ignored-dir/x + [1] From a907a1a6b9710d829d108437a84a0d35faba65fd Mon Sep 17 00:00:00 2001 From: Jeremie Dimino Date: Thu, 10 May 2018 16:34:05 +0100 Subject: [PATCH 11/11] Fix: don't read dune files in raw data sub-trees --- src/file_tree.ml | 81 ++++++++++++++++++++++++++---------------------- 1 file changed, 44 insertions(+), 37 deletions(-) diff --git a/src/file_tree.ml b/src/file_tree.ml index 41c19dfb649..1bb624bcea5 100644 --- a/src/file_tree.ml +++ b/src/file_tree.ml @@ -204,45 +204,52 @@ let load ?(extra_ignored_subtrees=Path.Set.empty) path = Left fn) in let files = String.Set.of_list files in - let dune_file = - match List.filter ["dune"; "jbuild"] ~f:(String.Set.mem files) with - | [] -> None - | [fn] -> Some (Dune_file.load (Path.relative path fn)) - | _ -> - die "Directory %s has both a 'dune' and 'jbuild' file.\n\ - This is not allowed" - (Path.to_string_maybe_quoted path) - in let dune_file, dune_fs = - match dune_file with - | None | Some (Ocaml_script _) -> (dune_file, None) - | Some (Plain { path; sexps }) -> - let fs_stanzas, other = - List.partition_map sexps ~f:(function - | List (loc, Atom (_, A "fs") :: sexps) -> Left (loc, sexps) - | x -> Right x) - in - match fs_stanzas with - | [] -> (dune_file, None) - | _ :: (loc, _) :: _ -> - Loc.fail loc "Too many fs stanzas." - | [(loc, sexps)] -> - (Some (Plain { path; sexps = other }), Some (loc, Dune_fs.t sexps)) - in - let dune_fs = - if String.Set.mem files "jbuild-ignore" then - let file = Path.relative path "jbuild-ignore" in - match dune_fs with - | None -> - Dune_fs.load_jbuild_ignore file - | Some (loc, _) -> - Loc.fail loc - "It is not allowed to have both a fs stanza and a \ - jbuild-ignore file in the same directory." + if raw_data then + (None, Dune_fs.default) else - match dune_fs with - | None -> Dune_fs.default - | Some (_, x) -> x + let dune_file = + match List.filter ["dune"; "jbuild"] ~f:(String.Set.mem files) with + | [] -> None + | [fn] -> Some (Dune_file.load (Path.relative path fn)) + | _ -> + die "Directory %s has both a 'dune' and 'jbuild' file.\n\ + This is not allowed" + (Path.to_string_maybe_quoted path) + in + let dune_file, dune_fs = + match dune_file with + | None | Some (Ocaml_script _) -> (dune_file, None) + | Some (Plain { path; sexps }) -> + let fs_stanzas, other = + List.partition_map sexps ~f:(function + | List (loc, Atom (_, A "fs") :: sexps) -> Left (loc, sexps) + | x -> Right x) + in + match fs_stanzas with + | [] -> (dune_file, None) + | _ :: (loc, _) :: _ -> + Loc.fail loc "Too many fs stanzas." + | [(loc, sexps)] -> + (Some (Plain { path; sexps = other }), + Some (loc, Dune_fs.t sexps)) + in + let dune_fs = + if String.Set.mem files "jbuild-ignore" then + let file = Path.relative path "jbuild-ignore" in + match dune_fs with + | None -> + Dune_fs.load_jbuild_ignore file + | Some (loc, _) -> + Loc.fail loc + "It is not allowed to have both a fs stanza and a \ + jbuild-ignore file in the same directory." + else + match dune_fs with + | None -> Dune_fs.default + | Some (_, x) -> x + in + (dune_file, dune_fs) in let files = String.Set.filter files ~f:(fun fn ->