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

Refactor OcamlDep from Native and BuckleScript backend #62

Merged
merged 1 commit into from
Sep 30, 2016
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
42 changes: 1 addition & 41 deletions src/bucklescript.re
Original file line number Diff line number Diff line change
Expand Up @@ -16,46 +16,6 @@ let jsOutput =
(targets == [] ? ["index"] : targets) |>
List.map f::(fun t => rel dir::(rel dir::buildDirRoot (tsl topLibName)) (t ^ ".js"));

/* Wrapper for the CLI `ocamldep`. Take the output, process it a bit, and pretend we've just called a regular
ocamldep OCaml function. Note: the `ocamldep` utility doesn't give us enough info for fine, accurate module
tracking in the presence of `open` */
let ocamlDep sourcePath::sourcePath => {
let flag = isInterface sourcePath ? "-intf" : "-impl";
/* seems like refmt intelligently detects source code type (re/ml) */
let getDepAction () =>
bashf
"ocamldep -pp refmt -ppx node_modules/.bin/bsppx -ml-synonym .re -mli-synonym .rei -modules -one-line %s %s 2>&1; (exit ${PIPESTATUS[0]})"
flag
(tsp sourcePath);
let action = Dep.action_stdout (Dep.path sourcePath |> mapD getDepAction);
let processRawString string =>
switch (String.strip string |> String.split on::':') {
| [original, deps] => (
rel dir::Path.the_root original,
String.split deps on::' ' |> List.filter f::nonBlank |> List.map f::(fun m => Mod m)
)
| _ => failwith "expected exactly one ':' in ocamldep output line"
};
Dep.map action processRawString
};

/* Get only the dependencies on sources in the current library. */
let ocamlDepCurrentSources sourcePath::sourcePath paths::paths =>
ocamlDep sourcePath::sourcePath |>
mapD (
fun (original, deps) => {
let originalModule = pathToModule original;
/* Dedupe, because we might have foo.re and foo.rei */
let sourceModules = List.map paths f::pathToModule |> List.dedup;
/* If the current file's Foo.re, and it depend on Foo, then it's certainly not depending on
itself, which means that Foo either comes from a third-party module (which we can ignore
here), or is a nested module from an `open`ed module, which ocamldep would have detected and
returned in this list. */
List.filter deps f::(fun m => m != originalModule) |>
List.filter f::(fun m => List.exists sourceModules f::(fun m' => m == m'))
}
);

/* the module alias file takes the current library foo's first-party sources, e.g. A.re, B.re, and turn them
into a foo.ml file whose content is:
module A = Foo__A;
Expand Down Expand Up @@ -192,7 +152,7 @@ let compileSourcesScheme

/** Compute build graph (targets, dependencies) for the current path */
let compilePathScheme path =>
ocamlDepCurrentSources sourcePath::path paths::sourcePaths |>
OcamlDep.ocamlDepCurrentSources sourcePath::path paths::sourcePaths |>
mapD (
fun firstPartyDeps => {
let isInterface' = isInterface path;
Expand Down
62 changes: 2 additions & 60 deletions src/native.re
Original file line number Diff line number Diff line change
Expand Up @@ -22,64 +22,6 @@ let binaryOutput = rel dir::(rel dir::buildDirRoot (tsl topLibName)) "app.out";

let jsOutput = rel dir::(rel dir::buildDirRoot (tsl topLibName)) "app.js";

/* Wrapper for the CLI `ocamldep`. Take the output, process it a bit, and pretend we've just called a regular
ocamldep OCaml function. Note: the `ocamldep` utility doesn't give us enough info for fine, accurate module
tracking in the presence of `open` */
let ocamlDep sourcePath::sourcePath => {
let flag = isInterface sourcePath ? "-intf" : "-impl";
/* seems like refmt intelligently detects source code type (re/ml) */
let getDepAction () =>
bashf
"ocamldep -pp refmt -ml-synonym .re -mli-synonym .rei -modules -one-line %s %s 2>&1 | berror; (exit ${PIPESTATUS[0]})"
flag
(tsp sourcePath);
let action = Dep.action_stdout (Dep.path sourcePath |> mapD getDepAction);
let processRawString string =>
switch (String.strip string |> String.split on::':') {
| [original, deps] => (
rel dir::Path.the_root original,
String.split deps on::' ' |> List.filter f::nonBlank |> List.map f::(fun m => Mod m)
)
| _ => failwith "expected exactly one ':' in ocamldep output line"
};
Dep.map action processRawString
};

/* Get only the dependencies on sources in the current library. */
let ocamlDepCurrentSources sourcePath::sourcePath paths::paths =>
ocamlDep sourcePath::sourcePath |>
mapD (
fun (original, deps) => {
let originalModule = pathToModule original;
/* Dedupe, because we might have foo.re and foo.rei */
let sourceModules = List.map paths f::pathToModule |> List.dedup;
/* If the current file's Foo.re, and it depend on Foo, then it's certainly not depending on
itself, which means that Foo either comes from a third-party module (which we can ignore
here), or is a nested module from an `open`ed module, which ocamldep would have detected and
returned in this list. */
List.filter deps f::(fun m => m != originalModule) |>
List.filter f::(fun m => List.exists sourceModules f::(fun m' => m == m'))
}
);

/* Used to compile a library file. The compile command requires files to be passed in order. If A requires B
but B is passed after A in the command, the compilation will fail with e.g. "module B not found" when
compiling A */
let sortPathsTopologically paths::paths => {
let pathsAsModulesOriginalCapitalization =
List.map paths f::(fun path => (pathToModule path, path));
let pathsAsModules = List.map pathsAsModulesOriginalCapitalization f::fst;
let moduleDepsForPathsD =
paths |> List.map f::(fun path => ocamlDepCurrentSources sourcePath::path paths::paths) |> Dep.all;
moduleDepsForPathsD |>
mapD (
fun moduleDepsForPaths =>
List.zip_exn pathsAsModules moduleDepsForPaths |> topologicalSort |>
List.map f::(fun m => List.Assoc.find_exn pathsAsModulesOriginalCapitalization m)
)
};


/**
When we build src folder of any package, we convert the package name in camelCase and
use it for modules alias. For the top level src, we simply module alias it with `Src`.
Expand Down Expand Up @@ -238,7 +180,7 @@ let compileSourcesScheme

/** Compute build graph (targets, dependencies) for the current path */
let compileEachSourcePath path =>
ocamlDepCurrentSources sourcePath::path paths::sourcePaths |>
OcamlDep.ocamlDepCurrentSources sourcePath::path paths::sourcePaths |>
mapD (
fun firstPartyDeps => {
let isInterface' = isInterface path;
Expand Down Expand Up @@ -492,7 +434,7 @@ let compileLibScheme
fun unsortedPaths =>
/* compute all the subdirectories */
/** Construct schemes for modules alias, source files and cma artifact/final executable **/
sortPathsTopologically paths::unsortedPaths |>
OcamlDep.sortPathsTopologically paths::unsortedPaths |>
mapD (
fun sortedPaths => Scheme.all [
moduleAliasLibScheme
Expand Down
75 changes: 75 additions & 0 deletions src/ocamlDep.re
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/*
* vim: set ft=rust:
* vim: set ft=reason:
*/
open Core.Std;

let module Dep = Jenga_lib.Api.Dep;

let module Path = Jenga_lib.Api.Path;

let module Action = Jenga_lib.Api.Action;

open Utils;


/* Wrapper for the CLI `ocamldep`. Take the output, process it a bit, and pretend we've just called a regular
ocamldep OCaml function. Note: the `ocamldep` utility doesn't give us enough info for fine, accurate module
tracking in the presence of `open` */
let ocamlDep sourcePath::sourcePath => {
let flag = isInterface sourcePath ? "-intf" : "-impl";
let ppx = backend == "bucklescript" ? "-ppx node_modules/bs-platform/bin/bsppx" : "";
let berror = backend == "bucklescript" ? "" : "| berror";
/* seems like refmt intelligently detects source code type (re/ml) */
let getDepAction () =>
bashf
"ocamldep -pp refmt %s -ml-synonym .re -mli-synonym .rei -modules -one-line %s %s 2>&1 %s; (exit ${PIPESTATUS[0]})"
ppx
flag
(tsp sourcePath)
berror;
let action = Dep.action_stdout (Dep.path sourcePath |> mapD getDepAction);
let processRawString string =>
switch (String.strip string |> String.split on::':') {
| [original, deps] => (
rel dir::Path.the_root original,
String.split deps on::' ' |> List.filter f::nonBlank |> List.map f::(fun m => Mod m)
)
| _ => failwith "expected exactly one ':' in ocamldep output line"
};
Dep.map action processRawString
};

/* Get only the dependencies on sources in the current library. */
let ocamlDepCurrentSources sourcePath::sourcePath paths::paths =>
ocamlDep sourcePath::sourcePath |>
mapD (
fun (original, deps) => {
let originalModule = pathToModule original;
/* Dedupe, because we might have foo.re and foo.rei */
let sourceModules = List.map paths f::pathToModule |> List.dedup;
/* If the current file's Foo.re, and it depend on Foo, then it's certainly not depending on
itself, which means that Foo either comes from a third-party module (which we can ignore
here), or is a nested module from an `open`ed module, which ocamldep would have detected and
returned in this list. */
List.filter deps f::(fun m => m != originalModule) |>
List.filter f::(fun m => List.exists sourceModules f::(fun m' => m == m'))
}
);

/* Used to compile a library file. The compile command requires files to be passed in order. If A requires B
but B is passed after A in the command, the compilation will fail with e.g. "module B not found" when
compiling A */
let sortPathsTopologically paths::paths => {
let pathsAsModulesOriginalCapitalization =
List.map paths f::(fun path => (pathToModule path, path));
let pathsAsModules = List.map pathsAsModulesOriginalCapitalization f::fst;
let moduleDepsForPathsD =
paths |> List.map f::(fun path => ocamlDepCurrentSources sourcePath::path paths::paths) |> Dep.all;
moduleDepsForPathsD |>
mapD (
fun moduleDepsForPaths =>
List.zip_exn pathsAsModules moduleDepsForPaths |> topologicalSort |>
List.map f::(fun m => List.Assoc.find_exn pathsAsModulesOriginalCapitalization m)
)
};