forked from ocaml/dune
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Towards completion of ocaml#159 Signed-off-by: Shon Feder <[email protected]>
- Loading branch information
Showing
5 changed files
with
262 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,196 @@ | ||
open! Stdune | ||
open! Import | ||
|
||
module Project = struct | ||
module Kind = struct | ||
type t = | ||
| Library | ||
| Executable | ||
|
||
let to_string = function | ||
| Library -> "library" | ||
| Executable -> "executable" | ||
end | ||
end | ||
|
||
module File = struct | ||
type t = | ||
{ path: Path.t | ||
; name: string | ||
; content: string } | ||
|
||
let make path name content = | ||
{path; name; content} | ||
|
||
let full_path {path; name; _} = | ||
Path.(append path (of_string name)) | ||
|
||
let create file = | ||
let path = full_path file in | ||
Io.write_file ~binary:false path file.content | ||
end | ||
|
||
module type InitContext = sig | ||
val kind : Project.Kind.t | ||
val pname : string | ||
val dir : Path.t | ||
end | ||
|
||
module type ProjectDir = sig | ||
val dir : Path.t | ||
val files : File.t list | ||
end | ||
|
||
module Proj (C : InitContext) : ProjectDir = struct | ||
let dir = C.dir | ||
let make_file = File.make C.dir | ||
|
||
let dune_project = | ||
let lang_stanza = Dune_project.lang_stanza () in | ||
let name = Dune_project.filename in | ||
let content = Printf.sprintf "\ | ||
%s | ||
(name %s) | ||
(version 0.1) | ||
" lang_stanza C.pname | ||
in | ||
make_file name content | ||
|
||
let package_opam = | ||
let name = Printf.sprintf "%s.opam" C.pname in | ||
make_file name "" | ||
|
||
let files = [dune_project; package_opam] | ||
end | ||
|
||
module Lib (C : InitContext) : ProjectDir = struct | ||
let dir = Path.(append C.dir (of_string "src")) | ||
let make_file = File.make dir | ||
|
||
let lib_dune = | ||
let name = "dune" in | ||
let content = Printf.sprintf "\ | ||
;; Uncomment stanzas to include their functionality | ||
(library | ||
(name %s) | ||
(public_name %s) | ||
;; (inline_tests) | ||
;; (libraries core) | ||
;; (preprocess (pps ppx_inline_test)) | ||
) | ||
" C.pname C.pname | ||
in | ||
make_file name content | ||
|
||
let lib_ml = | ||
let name = "lib.ml" in | ||
let content = {|let hello name = Printf.sprintf "Hello, %s" name | ||
|} in | ||
make_file name content | ||
|
||
let files = [lib_dune; lib_ml] | ||
end | ||
|
||
module Bin (C : InitContext) : ProjectDir = struct | ||
let dir = Path.(append C.dir (of_string "bin")) | ||
let make_file = File.make dir | ||
|
||
let bin_dune = | ||
let name = "dune" in | ||
let content = Printf.sprintf "\ | ||
(executable | ||
(public_name %s) | ||
(name main) | ||
(libraries %s)) | ||
" C.pname C.pname | ||
in | ||
make_file name content | ||
|
||
let bin_ml = | ||
let lib_module_name = String.capitalize_ascii C.pname in | ||
let name = "main.ml" in | ||
let content = | ||
Printf.sprintf | ||
{|let () = print_endline (%s.Lib.hello "project %s") | ||
|} | ||
lib_module_name C.pname | ||
in | ||
make_file name content | ||
|
||
let files = [bin_dune; bin_ml] | ||
end | ||
|
||
let make_project_dir_exn (module D : ProjectDir) = | ||
if Path.exists D.dir then | ||
Exn.fatalf "A project directory cannot be created: \ | ||
A directory named %s already exists" | ||
(Path.to_string D.dir); | ||
|
||
try Path.mkdir_p D.dir with | ||
| Unix.(Unix_error (EACCES, _, _)) -> | ||
Exn.fatalf "A project directory cannot be created: \ | ||
Lacking permissions needed to create directory %s" | ||
(Path.to_string D.dir) | ||
|
||
let make_project_dir_files (module D : ProjectDir) = | ||
List.iter ~f:File.create D.files | ||
|
||
let validate_project_name name = | ||
match Lib_name.Local.of_string name with | ||
| Ok _ -> () | ||
| _ -> Exn.fatalf "A project named '%s' cannot be created because it is an %s" | ||
name Lib_name.Local.invalid_message | ||
|
||
let make_context pname kind path = | ||
let module C = struct | ||
let kind = kind | ||
let pname = pname | ||
let dir = | ||
match path with | ||
| None -> Path.of_filename_relative_to_initial_cwd pname | ||
| Some path -> | ||
if Filename.is_relative path | ||
then Path.of_filename_relative_to_initial_cwd path | ||
else Path.of_string path | ||
end | ||
in | ||
(module C : InitContext) | ||
|
||
let make_project_dirs (module C : InitContext) = | ||
(* dirs that are always included in a project *) | ||
let project_dirs : (module ProjectDir) list = | ||
[ | ||
(module Proj (C)); (* The project dir should always be first *) | ||
(module Lib (C)); | ||
] | ||
in | ||
let project_dirs = match C.kind with | ||
| Project.Kind.Library -> project_dirs | ||
| Project.Kind.Executable -> project_dirs @ [(module Bin (C))] | ||
in | ||
List.iter ~f:make_project_dir_exn project_dirs; | ||
List.iter ~f:make_project_dir_files project_dirs | ||
|
||
let print_completion (module C : InitContext) = | ||
Errors.kerrf ~f:print_to_console | ||
"@{<ok>Success@}: %s %s project initialized\n" | ||
C.pname (Project.Kind.to_string C.kind) | ||
|
||
let print_hints (module C : InitContext) = | ||
let path = Path.to_string C.dir in | ||
match C.kind with | ||
| Project.Kind.Library -> | ||
Errors.kerrf ~f:print_to_console | ||
"@{<kwd>cd %s && dune build@} # to build the project\n" | ||
path | ||
| Project.Kind.Executable -> | ||
Errors.kerrf ~f:print_to_console | ||
"@{<kwd>cd %s && dune exec %s@} # to build and run the project\n" | ||
path C.pname | ||
|
||
let make_project pname kind path = | ||
validate_project_name pname; | ||
let (module C) = make_context pname kind path in | ||
make_project_dirs (module C); | ||
print_completion (module C); | ||
print_hints (module C) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
open! Stdune | ||
|
||
(** Supported kinds of projects for initialization *) | ||
module Project : sig | ||
module Kind : sig | ||
type t = | ||
| Library | ||
| Executable | ||
end | ||
end | ||
|
||
(** [make_project pname kind path] will make a project of the given [kind] | ||
with the supplied [pname], at the optional [path] (defaulting to the | ||
current working directory if the [path] is [None]). *) | ||
val make_project : string -> Project.Kind.t -> string option -> unit |