Skip to content

Commit 5aad207

Browse files
authored
Add hello command (#114)
1 parent 485a332 commit 5aad207

26 files changed

+514
-3
lines changed

CHANGES.md

+1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
## Added
44

5+
- Added an `hello` command to generate a tutorial project
56
- Added a `parse_binaries` stanza that can be `true` to force Spin to parse binary files
67
- Added a `raw_files` stanza that takes a list of file or glob expressions to instruct Spin to copy files instead of parsing them
78
- Added a new `c-bindings` template for C bindings using `ctypes`

bin/commands/cmd_hello.ml

+67
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
open Spin
2+
3+
let run ~path =
4+
let open Result.Syntax in
5+
let path = Option.value path ~default:Filename.current_dir_name in
6+
let* () =
7+
try
8+
match Sys.readdir path with
9+
| [||] ->
10+
Ok ()
11+
| _ ->
12+
Error
13+
(Spin_error.failed_to_generate "The output directory is not empty.")
14+
with
15+
| Sys_error _ ->
16+
Sys.mkdir_p path;
17+
Ok ()
18+
in
19+
try
20+
let* template = Template.read (Template.Official Spin_template.hello) in
21+
Template.generate ~path template
22+
with
23+
| Sys.Break | Failure _ ->
24+
exit 1
25+
| e ->
26+
raise e
27+
28+
(* Command line interface *)
29+
30+
open Cmdliner
31+
32+
let doc = "Generate a tutorial project in the given directory"
33+
34+
let sdocs = Manpage.s_common_options
35+
36+
let exits = Common.exits
37+
38+
let envs = Common.envs
39+
40+
let man_xrefs = [ `Main; `Cmd "new" ]
41+
42+
let man =
43+
[ `S Manpage.s_description
44+
; `P
45+
"$(tname) generates a tutorial project. This is useful if this is your \
46+
first project with OCaml and you want to learn by example."
47+
; `P
48+
"If you are already familiar with the typical OCaml development \
49+
environment, use spin-new(1) instead."
50+
]
51+
52+
let info = Term.info "hello" ~doc ~sdocs ~exits ~envs ~man ~man_xrefs
53+
54+
let term =
55+
let open Common.Syntax in
56+
let+ _term = Common.term
57+
and+ path =
58+
let doc =
59+
"The path where the project will be generated. If absent, the project \
60+
will be generated in the current working directory."
61+
in
62+
let docv = "PATH" in
63+
Arg.(value & pos 0 (some string) None & info [] ~doc ~docv)
64+
in
65+
run ~path |> Common.handle_errors
66+
67+
let cmd = term, info

bin/commands/cmd_new.ml

-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@ let run ~ignore_config ~use_defaults ~template ~path =
3737
in
3838
match Template.source_of_string template with
3939
| Some source ->
40-
let open Result.Syntax in
4140
(try
4241
let* template = Template.read ?context ~use_defaults source in
4342
Template.generate ~path template

bin/main.ml

+2-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ open Cmdliner
22

33
let () = Printexc.record_backtrace true
44

5-
let cmds = [ Cmd_config.cmd; Cmd_ls.cmd; Cmd_new.cmd ]
5+
let cmds = [ Cmd_config.cmd; Cmd_ls.cmd; Cmd_new.cmd; Cmd_hello.cmd ]
66

77
let run () =
88
let message =
@@ -16,6 +16,7 @@ Available Commands:
1616
config Update the current user's configuration
1717
ls List the official templates
1818
new Generate a new project from a template
19+
hello Generate the tutorial project
1920

2021
Useful options:
2122
--help Show manual page

script/demo-record.sh

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,4 @@ cd "$TEMP_DIR"
1313

1414
asciinema rec -c "$DIR/demo-emulate.sh"
1515

16-
# cat /var/folders/1k/w8wtfpk909s_mvn_72q6d2p40000gn/T/tmpl3vpup57-ascii.cast | svg-term --out "doc/demo.svg" --window --no-cursor --width 80 --height 24
16+
# cat /var/folders/1k/w8wtfpk909s_mvn_72q6d2p40000gn/T/tmp8dhvupt4-ascii.cast | svg-term --out "doc/demo.svg" --window --no-cursor --width 80 --height 24

template/dune

+8
Original file line numberDiff line numberDiff line change
@@ -51,3 +51,11 @@
5151
(source_tree js)))
5252
(action
5353
(run %{bin:ocaml-crunch} -m plain js -o %{targets})))
54+
55+
(rule
56+
(targets hello.ml)
57+
(deps
58+
(:data
59+
(source_tree hello)))
60+
(action
61+
(run %{bin:ocaml-crunch} -m plain hello -o %{targets})))

template/hello/README.md

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# bin
2+
3+
Native project containing a binary.
4+
5+
```bash
6+
spin new bin
7+
```

template/hello/spin

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
(name hello)
2+
3+
(description "Tutorial template without any configuration")
4+
5+
(post_gen
6+
(actions
7+
(run make deps)
8+
(run make build))
9+
(message "🎁 Installing packages globally. This might take a couple minutes."))
10+
11+
(example_commands
12+
(commands
13+
("make deps" "Download runtime and development dependencies.")
14+
("make build" "Build the dependencies and the project.")
15+
("make test" "Starts the test runner.")))

template/hello/template/.gitignore

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# Dune generated files
2+
_build/
3+
*.install
4+
5+
# Merlin configuring file for Vim and Emacs
6+
.merlin
7+
8+
# Local OPAM switch
9+
_opam/

template/hello/template/.ocamlformat

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# Pin the version of ocamlformat. The formatting will fail if a version other than 0.18.0 is used.
2+
# This ensures that the formatting does not depend on the specific version of ocamlformat developers use.
3+
version = 0.18.0
4+
# The conventional profile is fairly standard, but you can also try `sparse` or `janestreet`.
5+
profile = conventional
6+
# Format the code source found in docstrings
7+
parse-docstrings = true
8+
# Insert break lines for docstring that are longer than 80 characters.
9+
wrap-comments = true

template/hello/template/LICENSE

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
ISC License
2+
3+
Copyright (c) 2021 Your name
4+
5+
Permission to use, copy, modify, and/or distribute this software for any
6+
purpose with or without fee is hereby granted, provided that the above
7+
copyright notice and this permission notice appear in all copies.
8+
9+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10+
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11+
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12+
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13+
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14+
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15+
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

template/hello/template/Makefile

+77
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
# This Makefile offers simpler frontend to the different tools for common development tasks.
2+
# You'll see that all of the commands are prepended by `opam exec -- ...` to run use the
3+
# tools installed in your Opam switch without having to run `eval $(opam env)`
4+
.DEFAULT_GOAL := all
5+
6+
ARGS := $(wordlist 2,$(words $(MAKECMDGOALS)),$(MAKECMDGOALS))
7+
$(eval $(ARGS):;@:)
8+
9+
.PHONY: all
10+
all:
11+
opam exec -- dune build --root . @install
12+
13+
.PHONY: deps
14+
deps: ## Install development dependencies
15+
opam install -y dune-release ocamlformat utop ocaml-lsp-server
16+
opam install --deps-only --with-test --with-doc -y .
17+
18+
.PHONY: create_switch
19+
create_switch: ## Create an opam switch without any dependency
20+
opam switch create . --no-install
21+
22+
.PHONY: switch
23+
switch: ## Create an opam switch and install development dependencies
24+
opam install . --deps-only --with-doc --with-test
25+
opam install -y dune-release ocamlformat utop ocaml-lsp-server
26+
27+
.PHONY: lock
28+
lock: ## Generate a lock file
29+
opam lock -y .
30+
31+
.PHONY: build
32+
build: ## Build the project, including non installable libraries and executables
33+
opam exec -- dune build --root .
34+
35+
.PHONY: install
36+
install: all ## Install the packages on the system
37+
opam exec -- dune install --root .
38+
39+
.PHONY: start
40+
start: all ## Run the produced executable
41+
opam exec -- dune exec --root . bin/main.exe $(ARGS)
42+
43+
.PHONY: test
44+
test: ## Run the unit tests
45+
opam exec -- dune runtest --root .
46+
47+
.PHONY: clean
48+
clean: ## Clean build artifacts and other generated files
49+
opam exec -- dune clean --root .
50+
51+
.PHONY: doc
52+
doc: ## Generate odoc documentation
53+
opam exec -- dune build --root . @doc
54+
55+
.PHONY: servedoc
56+
servedoc: doc ## Open odoc documentation with default web browser
57+
open _build/default/_doc/_html/index.html
58+
59+
.PHONY: fmt
60+
fmt: ## Format the codebase with ocamlformat
61+
opam exec -- dune build --root . --auto-promote @fmt
62+
63+
.PHONY: watch
64+
watch: ## Watch for the filesystem and rebuild on every change
65+
opam exec -- dune build --root . --watch
66+
67+
.PHONY: utop
68+
utop: ## Run a REPL and link with the project's libraries
69+
opam exec -- dune utop --root . lib -- -implicit-bindings
70+
71+
.PHONY: release
72+
release: all ## Run the release script
73+
opam exec -- dune-release tag
74+
opam exec -- dune-release distrib
75+
opam exec -- dune-release publish distrib -y
76+
opam exec -- dune-release opam pkg
77+
opam exec -- dune-release opam submit --no-auto-open -y

template/hello/template/README.md

+89
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
# Hello
2+
3+
This project serves as a tutorial for your first OCaml project.
4+
5+
It will guide you through the typical project structure and OCaml development workflow.
6+
Although the organisation and tools used by the community vary, the ones used here are fairly standard and are the recommended way to work with OCaml with the [OCaml Platform](https://ocaml.org/platform/).
7+
8+
If you are already familiar with the setup and are looking to start a new project, you should probably use one of Spin's official template (list them with `spin ls`).
9+
10+
## Setup your development environment
11+
12+
You need Opam, you can install it by following [Opam's documentation](https://opam.ocaml.org/doc/Install.html).
13+
14+
With Opam installed, you can install the dependencies in a new local switch with:
15+
16+
```bash
17+
make switch
18+
```
19+
20+
Or globally, with:
21+
22+
```bash
23+
make deps
24+
```
25+
26+
Both of these commands will install the tools you typically need for you IDE (e.g. `ocaml-lsp-server`, `ocamlformat`).
27+
Once the installation is complete, you can open the project in your IDE with the `vscode-ocaml-platform` extension.
28+
29+
Finally, build the project with:
30+
31+
```bash
32+
make build
33+
```
34+
35+
### Running Binary
36+
37+
After building the project, you can run the main binary that is produced.
38+
39+
```bash
40+
make start
41+
```
42+
43+
### Running Tests
44+
45+
You can run the test compiled executable:
46+
47+
```bash
48+
make test
49+
```
50+
51+
### Building documentation
52+
53+
Documentation for the libraries in the project can be generated with:
54+
55+
```bash
56+
make doc
57+
make servedoc
58+
```
59+
60+
### Project Structure
61+
62+
The following snippet describes the project structure.
63+
64+
```text
65+
.
66+
├── bin/
67+
| Source for the executable. This links to the library defined in `lib/`.
68+
69+
├── lib/
70+
| Source for library of the project.
71+
72+
├── test/
73+
| Unit tests and integration tests.
74+
75+
├── dune-project
76+
| Dune file used to mark the root of the project and define project-wide parameters.
77+
| For the documentation of the syntax, see https://dune.readthedocs.io/en/stable/dune-files.html#dune-project
78+
79+
├── LICENSE
80+
81+
├── Makefile
82+
| Make file containing common development command.
83+
84+
├── README.md
85+
86+
└── hello.opam
87+
Opam package definition.
88+
To know more about creating and publishing opam packages, see https://opam.ocaml.org/doc/Packaging.html.
89+
```

template/hello/template/bin/dune

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
(executable
2+
; The name of th executable.
3+
; This is only the development name, but if user install the package, it will
4+
; be accessible with the name defined in `public_name`
5+
(name main)
6+
(public_name hello)
7+
; The executable depends on the library defined in `lib/`.
8+
; This make the module `Hello` and its exposed functions available in
9+
; all of the modules in this directory.
10+
(libraries hello))

template/hello/template/bin/main.ml

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
let () =
2+
let greeting = Hello.greet "world" in
3+
print_endline greeting

template/hello/template/bin/main.mli

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
(** Main entry point for our application.
2+
3+
This is left empty intentionnally, because executables should not have any
4+
interfaces. *)

template/hello/template/dune

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
; This defines some global environment configuration for Dune.
2+
; Here, we add some compiler flags in development to add warnings
3+
; on compilation, and fail the compilation when there are warnings.
4+
; If you want to deactivate some warning or error, you can add them
5+
; here, for instance `-w +A-48-42-44-4` will deactivate warning 4 in
6+
; addition to the ones already listed.
7+
8+
(env
9+
(dev
10+
(flags
11+
(:standard -w +A-48-42-44 -warn-error +A-3))))

0 commit comments

Comments
 (0)