Skip to content

Commit

Permalink
Finer dep for BuckleScript compilation
Browse files Browse the repository at this point in the history
  • Loading branch information
vramana committed Oct 9, 2016
1 parent e42d70b commit cff3baf
Show file tree
Hide file tree
Showing 2 changed files with 102 additions and 61 deletions.
102 changes: 49 additions & 53 deletions src/bucklescript.re
Original file line number Diff line number Diff line change
Expand Up @@ -106,13 +106,6 @@ let compileSourcesScheme
let thirdPartyNpmLibs = NpmDep.getThirdPartyNpmLibs libDir::libDir;
let thirdPartyOcamlfindLibNames = NpmDep.getThirdPartyOcamlfindLibs libDir::libDir;

/** FIXME Doesn't work with core_kernel **/
let ocamlfindPackagesStr =
switch thirdPartyOcamlfindLibNames {
| [] => ""
| libs => "-bs-package-include " ^ (libs |> List.map f::tsl |> String.concat sep::",")
};
/** Compute Module Alias dependencies for dependencies only */
let moduleAliasDep = Dep.all_unit (
(isTopLevelLib ? [] : ["cmi", "cmj", "cmt"]) |>
Expand All @@ -123,49 +116,43 @@ let compileSourcesScheme
source files being compiled. This is very coarse since in reality, we only depend on a few source
files of these third-party libs. But ocamldep isn't granular enough to give us this information
yet. */
let thirdPartiesArtifactsDep = Dep.all_unit (
List.map
thirdPartyNpmLibs
f::(
fun libName => {
/* if one of a third party library foo's source is Hi.re, then it resides in
`node_modules/foo/src/Hi.re`, and its cmj artifacts in `_build/foo/Foo__Hi.cmj` */
let thirdPartySrcPath = rel dir::(rel dir::nodeModulesRoot (tsl libName)) "src";
let thirdPartyBuildPathD path::path ext::ext =>
relD
dir::(rel dir::buildDirRoot (tsl libName))
(namespacedName libName::libName path::path ^ ext);

/** FIXME Temporary workaround for bucklescript bug */
let bsThirdPartyBuildPathD path::path ext::ext =>
relD
dir::(rel dir::buildDirRoot (tsl libName))
(bsNamespacedName libName::libName path::path ^ ext);

/** No need to glob `.rei/.mli`s here. We're only getting the file names to
construct cmj paths. We depend on cmj artifacts rather cmi artifacts because
cmi articfacts can be just generated with interface files and it is not sufficient
to build the target */
/* FIXME the js dependencies are Temporary and can just be cmj dependencies once
bucklescript bug is removed */
Dep.all (getSourceFiles dir::thirdPartySrcPath |> List.map f::Dep.return) |>
bindD (
fun thirdPartySources => Dep.all_unit (
List.map
thirdPartySources f::(fun path => thirdPartyBuildPathD path::path ext::".cmj") @
List.map
thirdPartySources f::(fun path => bsThirdPartyBuildPathD path::path ext::".js")
)
)
}
)
);
let computeNpmLibArtifacts libName => {
/* if one of a third party library foo's source is Hi.re, then it resides in
`node_modules/foo/src/Hi.re`, and its cmj artifacts in `_build/foo/Foo__Hi.cmj` */
let thirdPartySrcPath = rel dir::(rel dir::nodeModulesRoot (tsl libName)) "src";
let thirdPartyBuildPath path::path ext::ext =>
relD
dir::(rel dir::buildDirRoot (tsl libName))
(namespacedName libName::libName path::path ^ ext);

/** FIXME Temporary workaround for bucklescript bug */
let bsThirdPartyBuildPath path::path ext::ext =>
relD
dir::(rel dir::buildDirRoot (tsl libName))
(bsNamespacedName libName::libName path::path ^ ext);

/** No need to glob `.rei/.mli`s here. We're only getting the file names to
construct cmj paths. We depend on cmj artifacts rather cmi artifacts because
cmi articfacts can be just generated with interface files and it is not sufficient
to build the target */
/* FIXME the js dependencies are Temporary and can just be cmj dependencies once
bucklescript bug is removed */
getSourceFiles dir::thirdPartySrcPath |> (
fun thirdPartySources =>
List.map thirdPartySources f::(fun path => thirdPartyBuildPath path::path ext::".cmj") @
List.map thirdPartySources f::(fun path => bsThirdPartyBuildPath path::path ext::".js")
) |> Dep.all_unit
};

/** Compute build graph (targets, dependencies) for the current path */
let compilePathScheme path =>
OcamlDep.ocamlDepCurrentSources sourcePath::path paths::sourcePaths |>
OcamlDep.ocamlDepSource
sourcePath::path
paths::sourcePaths
npmPkgs::thirdPartyNpmLibs
ocamlfindPkgs::thirdPartyOcamlfindLibNames |>
mapD (
fun firstPartyDeps => {
fun (firstPartyDeps, npmPkgs, ocamlfindPkgs) => {
let isInterface' = isInterface path;
let hasInterface' = hasInterface sourcePaths::sourcePaths path;

Expand All @@ -176,9 +163,17 @@ let compileSourcesScheme

/** flag to include all the dependencies build dir's **/
let includeDir =
thirdPartyNpmLibs |> List.map f::(fun libName => "-I _build/" ^ tsl libName) |>
npmPkgs |> List.map f::(fun libName => "-I _build/" ^ tsl libName) |>
String.concat sep::" ";
/** Flag for including ocamlfind packages */
let ocamlfindPackagesStr =
switch ocamlfindPkgs {
| [] => ""
| libs => "-bs-package-include " ^ (libs |> List.map f::tsl |> String.concat sep::",")
};
/** Debug Info */
/* print_endline ("Path: " ^ tsp path);
print_endline "First Party Deps: ";
Expand Down Expand Up @@ -232,30 +227,31 @@ let compileSourcesScheme
/** FIXME is this comment valid?
compiling here only needs cmjs. If the interface signature doesn't change, ocaml doesn't need
to recompile the dependent modules. Win. */
let firstPartyArtifactDeps =
let firstPartyArtifacts =
sourcePaths |>
List.filter
f::(fun path => List.exists firstPartyDeps f::(fun m => m == pathToModule path)) |>
List.map f::(fun path => relD dir::buildDir (fileNameNoExtNoDir path ^ ".cmj"));
let firstPartyArtifactDeps =
let firstPartyArtifacts =
if hasInterface' {
[
/* We're a source file with an interface; include our own cmi as a dependency (our interface
file should compile before ourselves). */
relD dir::buildDir (fileNameNoExtNoDir path ^ ".cmi"),
...firstPartyArtifactDeps
...firstPartyArtifacts
]
} else {
firstPartyArtifactDeps
firstPartyArtifacts
};
let thirdPartyArtifacts = List.map npmPkgs f::computeNpmLibArtifacts |> Dep.all_unit;

/** The overall dependecies include the cmj artifacts of the both self and third party
and interface artifact if an interface exits **/
let deps = Dep.all_unit [
Dep.path path,
thirdPartiesArtifactsDep,
thirdPartyArtifacts,
moduleAliasDep,
...firstPartyArtifactDeps
...firstPartyArtifacts
];

/** Workaround for BuckleScript bug https://github.com/bloomberg/bucklescript/issues/757
Expand Down
61 changes: 53 additions & 8 deletions src/ocamlDep.re
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,11 @@ 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 ocamlDep source::source => {
let flag = isInterface source ? "-intf" : "-impl";
let ppx = rebelConfig.backend == "bucklescript" ? "-ppx bsppx.exe" : "";
let berror = rebelConfig.backend == "bucklescript" ? "" : "| berror";
/* seems like refmt intelligently detects source code type (re/ml) */
Expand All @@ -26,9 +25,9 @@ let ocamlDep sourcePath::sourcePath => {
"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)
(tsp source)
berror;
let action = Dep.action_stdout (Dep.path sourcePath |> mapD getDepAction);
let action = Dep.action_stdout (Dep.path source |> mapD getDepAction);
let processRawString string =>
switch (String.strip string |> String.split on::':') {
| [original, deps] => (
Expand All @@ -42,10 +41,10 @@ let ocamlDep sourcePath::sourcePath => {

/* Get only the dependencies on sources in the current library. */
let ocamlDepCurrentSources sourcePath::sourcePath paths::paths =>
ocamlDep sourcePath::sourcePath |>
ocamlDep source::sourcePath |>
mapD (
fun (original, deps) => {
let originalModule = pathToModule original;
fun (source, deps) => {
let originalModule = pathToModule source;
/* 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
Expand All @@ -57,6 +56,52 @@ let ocamlDepCurrentSources sourcePath::sourcePath paths::paths =>
}
);

exception Package_Not_Found string;

/* Get only the dependencies on sources in the current library. */
let ocamlDepSource
sourcePath::sourcePath
paths::paths
npmPkgs::npmPkgs
ocamlfindPkgs::ocamlfindPkgs =>
ocamlDep source::sourcePath |>
mapD (
fun (source, deps) => {
let originalModule = pathToModule source;

/** Dedupe, because we might have foo.re and foo.rei */
let sourceModules = List.map paths f::pathToModule |> List.dedup;
let npmPkgsModules = List.map npmPkgs f::libToModule;
let ocamlfindPkgsModules = List.map ocamlfindPkgs f::libToModule;

/** 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.fold
init::([], [], [])
f::(
fun (firstParty, npmPkgs', ocamlfindPkgs') m =>
if (List.exists sourceModules f::(fun m' => m == m')) {
(firstParty @ [m], npmPkgs', ocamlfindPkgs')
} else if (
List.exists npmPkgsModules f::(fun m' => m == m')
) {
let pos = List.foldi init::0 f::(fun i acc m' => m' == m ? acc + i : acc) npmPkgsModules;
(firstParty, npmPkgs' @ [ List.nth_exn npmPkgs pos ], ocamlfindPkgs')
} else if (
List.exists ocamlfindPkgsModules f::(fun m' => m == m')
) {
let pos = List.foldi init::0 f::(fun i acc m' => m' == m ? acc + i : acc) ocamlfindPkgsModules;
(firstParty, npmPkgs', ocamlfindPkgs' @ [ List.nth_exn ocamlfindPkgs pos ])
} else {
(firstParty, npmPkgs', ocamlfindPkgs')
}
)
}
);

/* 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 */
Expand Down

0 comments on commit cff3baf

Please sign in to comment.