Repository: diku-dk/smlpkg Branch: master Commit: 7cd3f2e95fdb Files: 149 Total size: 100.7 KB Directory structure: gitextract_lu3gl94a/ ├── .github/ │ └── workflows/ │ ├── main.yml │ └── release.yml ├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── default.nix ├── pkgtests/ │ ├── .gitignore │ ├── Makefile │ ├── README.md │ ├── futhark.pkg.0 │ ├── futhark.pkg.1 │ ├── futhark.pkg.10 │ ├── futhark.pkg.11 │ ├── futhark.pkg.12 │ ├── futhark.pkg.13 │ ├── futhark.pkg.14 │ ├── futhark.pkg.15 │ ├── futhark.pkg.16 │ ├── futhark.pkg.17 │ ├── futhark.pkg.18 │ ├── futhark.pkg.2 │ ├── futhark.pkg.3 │ ├── futhark.pkg.4 │ ├── futhark.pkg.5 │ ├── futhark.pkg.6 │ ├── futhark.pkg.7 │ ├── futhark.pkg.8 │ ├── futhark.pkg.9 │ ├── lib.10/ │ │ └── github.com/ │ │ └── athas/ │ │ ├── fut-bar/ │ │ │ └── bar.fut │ │ ├── fut-baz/ │ │ │ └── baz.fut │ │ ├── fut-foo/ │ │ │ └── foo.fut │ │ └── fut-foo@2/ │ │ └── foo.fut │ ├── lib.11/ │ │ └── github.com/ │ │ └── athas/ │ │ ├── fut-bar/ │ │ │ └── bar.fut │ │ ├── fut-baz/ │ │ │ └── baz.fut │ │ ├── fut-foo/ │ │ │ └── foo.fut │ │ └── fut-foo@2/ │ │ └── foo.fut │ ├── lib.12/ │ │ └── github.com/ │ │ └── athas/ │ │ ├── fut-bar/ │ │ │ └── bar.fut │ │ ├── fut-baz/ │ │ │ └── baz.fut │ │ ├── fut-foo/ │ │ │ └── foo.fut │ │ ├── fut-foo@2/ │ │ │ └── foo.fut │ │ └── fut-quux/ │ │ └── quux.fut │ ├── lib.13/ │ │ └── github.com/ │ │ └── athas/ │ │ ├── fut-bar/ │ │ │ └── bar.fut │ │ ├── fut-baz/ │ │ │ └── baz.fut │ │ ├── fut-foo/ │ │ │ └── foo.fut │ │ ├── fut-foo@2/ │ │ │ └── foo.fut │ │ └── fut-quux/ │ │ └── quux.fut │ ├── lib.14/ │ │ └── github.com/ │ │ └── athas/ │ │ ├── fut-bar/ │ │ │ └── bar.fut │ │ ├── fut-baz/ │ │ │ └── baz.fut │ │ ├── fut-foo/ │ │ │ └── foo.fut │ │ ├── fut-foo@2/ │ │ │ └── foo.fut │ │ └── fut-quux/ │ │ └── quux.fut │ ├── lib.15/ │ │ └── github.com/ │ │ └── athas/ │ │ ├── fut-bar/ │ │ │ └── bar.fut │ │ ├── fut-baz/ │ │ │ └── baz.fut │ │ ├── fut-foo/ │ │ │ └── foo.fut │ │ ├── fut-foo@2/ │ │ │ └── foo.fut │ │ └── fut-quux/ │ │ └── quux.fut │ ├── lib.16/ │ │ ├── github.com/ │ │ │ └── athas/ │ │ │ ├── fut-bar/ │ │ │ │ └── bar.fut │ │ │ ├── fut-baz/ │ │ │ │ └── baz.fut │ │ │ ├── fut-foo/ │ │ │ │ └── foo.fut │ │ │ ├── fut-foo@2/ │ │ │ │ └── foo.fut │ │ │ └── fut-quux/ │ │ │ └── quux.fut │ │ └── gitlab.com/ │ │ └── athas/ │ │ └── fut-gitlab/ │ │ └── gitlab.fut │ ├── lib.17/ │ │ └── github.com/ │ │ └── athas/ │ │ ├── fut-bar/ │ │ │ └── bar.fut │ │ ├── fut-baz/ │ │ │ └── baz.fut │ │ ├── fut-foo/ │ │ │ └── foo.fut │ │ ├── fut-foo@2/ │ │ │ └── foo.fut │ │ └── fut-quux/ │ │ └── quux.fut │ ├── lib.18/ │ │ ├── github.com/ │ │ │ └── athas/ │ │ │ ├── fut-bar/ │ │ │ │ └── bar.fut │ │ │ ├── fut-baz/ │ │ │ │ └── baz.fut │ │ │ ├── fut-foo/ │ │ │ │ └── foo.fut │ │ │ ├── fut-foo@2/ │ │ │ │ └── foo.fut │ │ │ └── fut-quux/ │ │ │ └── quux.fut │ │ └── gitlab.com/ │ │ └── athas/ │ │ └── fut-gitlab/ │ │ └── gitlab.fut │ ├── lib.2/ │ │ └── github.com/ │ │ └── athas/ │ │ └── fut-foo/ │ │ └── foo.fut │ ├── lib.3/ │ │ └── github.com/ │ │ └── athas/ │ │ └── fut-foo/ │ │ └── foo.fut │ ├── lib.4/ │ │ └── github.com/ │ │ └── athas/ │ │ ├── fut-baz/ │ │ │ └── baz.fut │ │ └── fut-foo/ │ │ └── foo.fut │ ├── lib.5/ │ │ └── github.com/ │ │ └── athas/ │ │ ├── fut-baz/ │ │ │ └── baz.fut │ │ └── fut-foo/ │ │ └── foo.fut │ ├── lib.6/ │ │ └── github.com/ │ │ └── athas/ │ │ ├── fut-bar/ │ │ │ └── bar.fut │ │ ├── fut-baz/ │ │ │ └── baz.fut │ │ └── fut-foo/ │ │ └── foo.fut │ ├── lib.7/ │ │ └── github.com/ │ │ └── athas/ │ │ ├── fut-bar/ │ │ │ └── bar.fut │ │ ├── fut-baz/ │ │ │ └── baz.fut │ │ └── fut-foo/ │ │ └── foo.fut │ ├── lib.8/ │ │ └── github.com/ │ │ └── athas/ │ │ ├── fut-bar/ │ │ │ └── bar.fut │ │ ├── fut-baz/ │ │ │ └── baz.fut │ │ └── fut-foo/ │ │ └── foo.fut │ ├── lib.9/ │ │ └── github.com/ │ │ └── athas/ │ │ ├── fut-bar/ │ │ │ └── bar.fut │ │ ├── fut-baz/ │ │ │ └── baz.fut │ │ └── fut-foo/ │ │ └── foo.fut │ └── test.sh ├── src/ │ ├── .gitignore │ ├── Makefile │ ├── futpkg.mlb │ ├── futpkg.sml │ ├── manifest/ │ │ ├── .gitignore │ │ ├── Makefile │ │ ├── lex.sig │ │ ├── lex.sml │ │ ├── manifest.mlb │ │ ├── manifest.sig │ │ ├── manifest.sml │ │ ├── test.mlb │ │ └── test.sml │ ├── parsecomb/ │ │ ├── .gitignore │ │ ├── Makefile │ │ ├── PARSE_COMB.sig │ │ ├── ParseComb.sml │ │ ├── REGION.sig │ │ ├── Region.sml │ │ └── parsecomb.mlb │ ├── pkg.mlb │ ├── pkg.sml │ ├── pkginfo.sig │ ├── pkginfo.sml │ ├── semver/ │ │ ├── .gitignore │ │ ├── Makefile │ │ ├── semver.mlb │ │ ├── semver.sig │ │ ├── semver.sml │ │ ├── test.mlb │ │ └── test.sml │ ├── smlpkg.mlb │ ├── smlpkg.sml │ ├── solve/ │ │ ├── .gitignore │ │ ├── Makefile │ │ ├── solve.sig │ │ ├── solve.sml │ │ ├── test.mlb │ │ └── test.sml │ ├── test/ │ │ ├── .gitignore │ │ ├── Makefile │ │ ├── futhark.pkg.test1.ok │ │ ├── futhark.pkg.test2.ok │ │ └── futhark.pkg.test3.ok │ └── util/ │ ├── .gitignore │ ├── Makefile │ ├── finmapeq.sig │ ├── finmapeq.sml │ ├── system.sig │ ├── system.sml │ ├── test.mlb │ ├── test.sml │ ├── test_system.sml │ ├── testfile.txt │ └── util.mlb └── version.txt ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/workflows/main.yml ================================================ # This is the smlpkg main workflow for building and testing smlpkg # on various architectures. The workflow contains two jobs called # "build-test-ubuntu" and "build-test-macos". It is triggered on # push or pull request events but only for the master branch. name: CI on: push: branches: [ master ] pull_request: branches: [ master ] # Allows you to run this workflow manually from the Actions tab workflow_dispatch: jobs: build-test-linux: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Install dependencies run: | sudo apt-get -qq update sudo apt-get install -y make - uses: diku-dk/install-mlkit@v1 with: version: 'latest' - name: Build run: | MLCOMP=mlkit make clean all bin_dist - name: Run tests run: | MLCOMP=mlkit make test build-test-macos: runs-on: macos-latest steps: - uses: actions/checkout@v3 - name: Install dependencies run: | brew install make brew install mlton - name: Build run: | MLCOMP=mlton make clean all bin_dist - name: Run tests run: | MLCOMP=mlton make test ================================================ FILE: .github/workflows/release.yml ================================================ name: Release on: push: tags: - 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10 jobs: build-test-release-linux: name: Build, test, and upload release binaries (linux) if: github.event.base_ref == 'refs/heads/master' runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v2 - name: Install dependencies run: | sudo apt-get -qq update sudo apt-get install -y mlton make - name: Build run: | MLCOMP=mlton make clean all bin_dist - name: Run tests run: | MLCOMP=mlton make test - name: Upload binaries to release uses: svenstaro/upload-release-action@v2 with: repo_token: ${{ secrets.GITHUB_TOKEN }} file: smlpkg-bin-dist-linux.tgz tag: ${{ github.ref }} overwrite: true build-test-release-macos: name: Build, test, and upload release binaries (macos) if: github.event.base_ref == 'refs/heads/master' runs-on: macos-latest steps: - uses: actions/checkout@v2 - name: Install dependencies run: | brew install make brew install mlton - name: Build run: | MLCOMP=mlton make clean all bin_dist - name: Run tests run: | MLCOMP=mlton make test - name: Upload binaries to release uses: svenstaro/upload-release-action@v2 with: repo_token: ${{ secrets.GITHUB_TOKEN }} file: smlpkg-bin-dist-darwin.tgz tag: ${{ github.ref }} overwrite: true ================================================ FILE: .gitignore ================================================ bin ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2020 Martin Elsman Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: Makefile ================================================ prefix ?= . INSTALLDIR ?= $(prefix)/bin INSTALL ?= install OS=$(shell uname -s | tr '[:upper:]' '[:lower:]') .PHONY: all all: src/smlpkg .PHONY: install: src/smlpkg mkdir -p $(INSTALLDIR) $(INSTALL) $< $(INSTALLDIR)/ .PHONY: test test: $(MAKE) -C src test $(MAKE) -C src/test test $(MAKE) -C pkgtests test .PHONY: clean clean: $(MAKE) -C src clean rm -rf *~ .*~ bin smlpkg-bin-* src/smlpkg: $(MAKE) -C src all # ----------------------------------------------------- # Target for building binary distribution for smlpkg # ----------------------------------------------------- BIN_DIST_DIR=smlpkg-bin-dist-$(OS) .PHONY: bin_dist bin_dist: src/smlpkg rm -rf smlpkg-bin-dist-* mkdir $(BIN_DIST_DIR) $(MAKE) install INSTALLDIR=$(BIN_DIST_DIR)/bin/ $(INSTALL) LICENSE $(BIN_DIST_DIR)/ echo 'Binary package for smlpkg.' > $(BIN_DIST_DIR)/README echo 'The sources are available at http://github.com/diku-dk/smlpkg' >> $(BIN_DIST_DIR)/README echo 'PREFIX?=/usr/local' > $(BIN_DIST_DIR)/Makefile echo 'INSTALL?=install' >> $(BIN_DIST_DIR)/Makefile echo '.PHONY: install' >> $(BIN_DIST_DIR)/Makefile echo 'install:' >> $(BIN_DIST_DIR)/Makefile echo "\t"'$$(INSTALL) bin/smlpkg $$(PREFIX)/bin/' >> $(BIN_DIST_DIR)/Makefile tar czvf $(BIN_DIST_DIR).tgz $(BIN_DIST_DIR) ================================================ FILE: README.md ================================================ # smlpkg [![CI](https://github.com/diku-dk/smlpkg/workflows/CI/badge.svg)](https://github.com/diku-dk/smlpkg/actions) This program constitutes a generic package manager for Standard ML libraries and programs. The package manager assumes nothing and knows nothing about the Standard ML compilers used and is thus quite generic. The package manager is centered around the notion of [semantic versioning](https://semver.org/) and supports packages that, for instance, are hosted on [GitHub](https://github.com/) and [GitLab](https://about.gitlab.com/). The package manager takes care of downloading and upgrading dependent packages and works well with the use of MLB files supported by Standard ML compilers such as [MLton](http://mlton.org/), [MLKit](http://elsman.com/mlkit/), and [SMLtoJs](http://www.smlserver.org/smltojs/). # Usage ## Adding a package ``` $ smlpkg add github.com/diku-dk/sml-random ``` This command modifies only `sml.pkg` and creates it if it does not already exist. ## Downloading required packages ``` $ smlpkg sync ``` This command populates the `lib` directory based on the packages listed in `sml.pkg`. ## Creating a new package ``` $ smlpkg init github.com/foo/bar ``` This command creates a file `sml.pkg` and initiates it with the given package name (`foo` should be a github user name or an organisation name and `bar` should be a repository name). You can now add code in the directory `lib/github.com/foo/bar/`. ## Releasing a package ``` $ git tag vX.Y.Z $ git push --tags ``` Remember to follow [semantic versioning](https://semver.org). Once a package has been released, other packages can safely add the package to their own source code tree using `smlpkg add` (see above). The lists below constitute some available packages. The lists are divided into packages that are generic (should work with any Standard ML compiler that supports basis files) and packages that are supported only for particular compilers (e.g., MLKit, Mlton, and `polymlb`). ## Generic packages * [![CI](https://github.com/diku-dk/futhark-data-sml/workflows/CI/badge.svg)](https://github.com/diku-dk/futhark-data-sml/actions) [github.com/diku-dk/futhark-data-sml](https://github.com/diku-dk/futhark-data-sml) * [![CI](https://github.com/diku-dk/futhark-server-sml/workflows/CI/badge.svg)](https://github.com/diku-dk/futhark-server-sml/actions) [github.com/diku-dk/futhark-server-sml](https://github.com/diku-dk/futhark-server-sml) * [![CI](https://github.com/diku-dk/sml-aplparse/workflows/CI/badge.svg)](https://github.com/diku-dk/sml-aplparse/actions) [github.com/diku-dk/sml-aplparse](https://github.com/diku-dk/sml-aplparse) * [![CI](https://github.com/diku-dk/sml-base64/workflows/CI/badge.svg)](https://github.com/diku-dk/sml-base64/actions) [github.com/diku-dk/sml-base64](https://github.com/diku-dk/sml-base64) * [![CI](https://github.com/diku-dk/sml-complex/workflows/CI/badge.svg)](https://github.com/diku-dk/sml-complex/actions) [github.com/diku-dk/sml-complex](https://github.com/diku-dk/sml-complex) * [![CI](https://github.com/diku-dk/sml-cstring/workflows/CI/badge.svg)](https://github.com/diku-dk/sml-cstring/actions) [github.com/diku-dk/sml-cstring](https://github.com/diku-dk/sml-cstring) * [![CI](https://github.com/diku-dk/sml-hashtable/workflows/CI/badge.svg)](https://github.com/diku-dk/sml-hashtable/actions) [github.com/diku-dk/sml-hashtable](https://github.com/diku-dk/sml-hashtable) * [![CI](https://github.com/diku-dk/sml-http/workflows/CI/badge.svg)](https://github.com/diku-dk/sml-http/actions) [github.com/diku-dk/sml-http](https://github.com/diku-dk/sml-http) * [![CI](https://github.com/diku-dk/sml-json/workflows/CI/badge.svg)](https://github.com/diku-dk/sml-json/actions) [github.com/diku-dk/sml-json](https://github.com/diku-dk/sml-json) * [![CI](https://github.com/diku-dk/sml-matrix/workflows/CI/badge.svg)](https://github.com/diku-dk/sml-matrix/actions) [github.com/diku-dk/sml-matrix](https://github.com/diku-dk/sml-matrix) * [![CI](https://github.com/diku-dk/sml-md5/workflows/CI/badge.svg)](https://github.com/diku-dk/sml-md5/actions) [github.com/diku-dk/sml-md5](https://github.com/diku-dk/sml-md5) * [![CI](https://github.com/diku-dk/sml-getopt/workflows/CI/badge.svg)](https://github.com/diku-dk/sml-getopt/actions) [github.com/diku-dk/sml-getopt](https://github.com/diku-dk/sml-getopt) * [![CI](https://github.com/diku-dk/sml-parse/workflows/CI/badge.svg)](https://github.com/diku-dk/sml-parse/actions) [github.com/diku-dk/sml-parse](https://github.com/diku-dk/sml-parse) * [![CI](https://github.com/diku-dk/sml-pickle/workflows/CI/badge.svg)](https://github.com/diku-dk/sml-pickle/actions) [github.com/diku-dk/sml-pickle](https://github.com/diku-dk/sml-pickle) * [![CI](https://github.com/diku-dk/sml-pretty/workflows/CI/badge.svg)](https://github.com/diku-dk/sml-pretty/actions) [github.com/diku-dk/sml-pretty](https://github.com/diku-dk/sml-pretty) * [![CI](https://github.com/diku-dk/sml-regexp/workflows/CI/badge.svg)](https://github.com/diku-dk/sml-regexp/actions) [github.com/diku-dk/sml-regexp](https://github.com/diku-dk/sml-regexp) * [![CI](https://github.com/diku-dk/sml-random/workflows/CI/badge.svg)](https://github.com/diku-dk/sml-random/actions) [github.com/diku-dk/sml-random](https://github.com/diku-dk/sml-random) * [![CI](https://github.com/diku-dk/sml-server/workflows/CI/badge.svg)](https://github.com/diku-dk/sml-server/actions) [github.com/diku-dk/sml-server](https://github.com/diku-dk/sml-server) * [![CI](https://github.com/diku-dk/sml-setmap/workflows/CI/badge.svg)](https://github.com/diku-dk/sml-setmap/actions) [github.com/diku-dk/sml-setmap](https://github.com/diku-dk/sml-setmap) * [![CI](https://github.com/diku-dk/sml-sort/workflows/CI/badge.svg)](https://github.com/diku-dk/sml-sort/actions) [github.com/diku-dk/sml-sort](https://github.com/diku-dk/sml-sort) * [![CI](https://github.com/diku-dk/sml-sha256/workflows/CI/badge.svg)](https://github.com/diku-dk/sml-sha256/actions) [github.com/diku-dk/sml-sha256](https://github.com/diku-dk/sml-sha256) * [![CI](https://github.com/diku-dk/sml-sobol/workflows/CI/badge.svg)](https://github.com/diku-dk/sml-sobol/actions) [github.com/diku-dk/sml-sobol](https://github.com/diku-dk/sml-sobol) * [![CI](https://github.com/diku-dk/sml-unicode/workflows/CI/badge.svg)](https://github.com/diku-dk/sml-unicode/actions) [github.com/diku-dk/sml-unicode](https://github.com/diku-dk/sml-unicode) * [![CI](https://github.com/diku-dk/sml-uref/workflows/CI/badge.svg)](https://github.com/diku-dk/sml-uref/actions) [github.com/diku-dk/sml-uref](https://github.com/diku-dk/sml-uref) * [github.com/pzel/sml-either](https://github.com/pzel/sml-either) * [github.com/shwestrick/sml-audio](https://github.com/shwestrick/sml-audio) * [github.com/shwestrick/sml-uri](https://github.com/shwestrick/sml-uri) * [github.com/shwestrick/sml-parseq](https://github.com/shwestrick/sml-parseq) ## MLKit packages * [![CI](https://github.com/melsman/mlkit-postgresql/workflows/CI/badge.svg)](https://github.com/melsman/mlkit-postgresql/actions) [github.com/melsman/mlkit-postgresql](https://github.com/melsman/mlkit-postgresql) * [![CI](https://github.com/melsman/mlkit-ssl-socket/workflows/CI/badge.svg)](https://github.com/melsman/mlkit-ssl-socket/actions) [github.com/melsman/mlkit-ssl-socket](https://github.com/melsman/mlkit-ssl-socket) * [![CI](https://github.com/diku-dk/sml-tigr/workflows/CI/badge.svg)](https://github.com/diku-dk/sml-tigr/actions) [github.com/diku-dk/sml-tigr](https://github.com/diku-dk/sml-tigr) ## Polymlb packages * [github.com/pzel/assert-polyml](https://github.com/pzel/assert-polyml) * [github.com/pzel/sqlite3-polyml](https://github.com/pzel/sqlite3-polyml) ## Design details See this [blog post on the design of the Futhark package manager](https://futhark-lang.org/blog/2018-08-03-the-present-futhark-package-manager.html). # Compilation To compile the package manager, you need a Standard ML compiler such as [MLton](http://mlton.org/) or [MLKit](http://elsman.com/mlkit/). ## Compilation using MLKit on macOS ``` $ brew install mlkit $ make all ``` ## Compilation using MLton ``` $ MLCOMP=mlton make clean all ``` # License This software is distributed under the [MIT LICENSE](LICENSE). The package manager is almost a complete port of the Futhark package manager, designed, and implemented in Haskell by Troels Henriksen. ================================================ FILE: default.nix ================================================ { pkgs ? import {}, }: pkgs.stdenv.mkDerivation rec { name = "smlpkg"; src = ./.; nativeBuildInputs = [ pkgs.mlton ]; checkInputs = [ pkgs.unzip ]; enableParallelBuilding = true; doCheck = true; # Set as an environment variable in all the phase scripts. MLCOMP = "mlton"; buildPhase = '' make all ''; installPhase = '' make install prefix=$out ''; # We cannot run the pkgtests, as Nix does not allow network # connections. checkPhase = '' make -C src test ''; } ================================================ FILE: pkgtests/.gitignore ================================================ lib futhark.pkg ================================================ FILE: pkgtests/Makefile ================================================ .PHONY: test test: ./test.sh .PHONY: clean clean: rm -rf lib futhark.pkg *~ ================================================ FILE: pkgtests/README.md ================================================ # futhark-pkg tests This directory contains a shell script (sorry) for testing futhark-pkg. This is done by serially performing package management operations, and after each operation comparing the resulting `lib` directory against an expected `lib` directory. It is distressingly awkward to write this using one of the normal Haskell test frameworks. The tests here are somewhat unstable, in that they depend on certain remote Git repositories to have specific contents. If they change, you have to change this test, too. ================================================ FILE: pkgtests/futhark.pkg.0 ================================================ package github.com/sturluson/testpkg require { } ================================================ FILE: pkgtests/futhark.pkg.1 ================================================ package github.com/sturluson/testpkg require { github.com/athas/fut-foo 0.1.0 #d285563c25c5152b1ae80fc64de64ff2775fa733 } ================================================ FILE: pkgtests/futhark.pkg.10 ================================================ package github.com/sturluson/testpkg require { github.com/athas/fut-baz 0.2.0 #44da85224224d37803976c1d30cedb1d2cd20b74 github.com/athas/fut-foo@2 2.0.0 #9459117fa75aea8fffc677294d25f273e894d19f } ================================================ FILE: pkgtests/futhark.pkg.11 ================================================ package github.com/sturluson/testpkg require { github.com/athas/fut-baz 0.2.0 #44da85224224d37803976c1d30cedb1d2cd20b74 github.com/athas/fut-foo@2 2.0.0 #9459117fa75aea8fffc677294d25f273e894d19f github.com/athas/fut-quux 0.0.0-20180801102532+b70028521e4dbcc286834b32ce82c1d2721a6209 } ================================================ FILE: pkgtests/futhark.pkg.12 ================================================ package github.com/sturluson/testpkg require { github.com/athas/fut-baz 0.2.0 #44da85224224d37803976c1d30cedb1d2cd20b74 github.com/athas/fut-foo@2 2.0.0 #9459117fa75aea8fffc677294d25f273e894d19f github.com/athas/fut-quux 0.0.0-20180801102532+b70028521e4dbcc286834b32ce82c1d2721a6209 } ================================================ FILE: pkgtests/futhark.pkg.13 ================================================ package github.com/sturluson/testpkg require { github.com/athas/fut-baz 0.2.0 #44da85224224d37803976c1d30cedb1d2cd20b74 github.com/athas/fut-foo@2 2.0.0 #9459117fa75aea8fffc677294d25f273e894d19f github.com/athas/fut-quux 0.0.0-20180801102533+dd5168df1b8a20cb0547a88afd4e4a6cc098e0f1 } ================================================ FILE: pkgtests/futhark.pkg.14 ================================================ package github.com/sturluson/testpkg require { github.com/athas/fut-baz 0.2.0 #44da85224224d37803976c1d30cedb1d2cd20b74 github.com/athas/fut-foo@2 2.0.0 #9459117fa75aea8fffc677294d25f273e894d19f github.com/athas/fut-quux 0.0.0-20180801102533+dd5168df1b8a20cb0547a88afd4e4a6cc098e0f1 } ================================================ FILE: pkgtests/futhark.pkg.15 ================================================ package github.com/sturluson/testpkg require { github.com/athas/fut-baz 0.2.0 #44da85224224d37803976c1d30cedb1d2cd20b74 github.com/athas/fut-foo@2 2.0.0 #9459117fa75aea8fffc677294d25f273e894d19f github.com/athas/fut-quux 0.0.0-20180801102533+dd5168df1b8a20cb0547a88afd4e4a6cc098e0f1 gitlab.com/athas/fut-gitlab 1.0.1 #631578b71d68381dd7461fc1d0c669cf84d0d5fe } ================================================ FILE: pkgtests/futhark.pkg.16 ================================================ package github.com/sturluson/testpkg require { github.com/athas/fut-baz 0.2.0 #44da85224224d37803976c1d30cedb1d2cd20b74 github.com/athas/fut-foo@2 2.0.0 #9459117fa75aea8fffc677294d25f273e894d19f github.com/athas/fut-quux 0.0.0-20180801102533+dd5168df1b8a20cb0547a88afd4e4a6cc098e0f1 gitlab.com/athas/fut-gitlab 1.0.1 #631578b71d68381dd7461fc1d0c669cf84d0d5fe } ================================================ FILE: pkgtests/futhark.pkg.17 ================================================ package github.com/sturluson/testpkg require { github.com/athas/fut-baz 0.2.0 #44da85224224d37803976c1d30cedb1d2cd20b74 github.com/athas/fut-foo@2 2.0.0 #9459117fa75aea8fffc677294d25f273e894d19f github.com/athas/fut-quux 0.0.0-20180801102533+dd5168df1b8a20cb0547a88afd4e4a6cc098e0f1 gitlab.com/athas/fut-gitlab 0.0.0-20180922095419+44bc83247e7b4995dd0c65acf3a72b70d6fe3efe } ================================================ FILE: pkgtests/futhark.pkg.18 ================================================ package github.com/sturluson/testpkg require { github.com/athas/fut-baz 0.2.0 #44da85224224d37803976c1d30cedb1d2cd20b74 github.com/athas/fut-foo@2 2.0.0 #9459117fa75aea8fffc677294d25f273e894d19f github.com/athas/fut-quux 0.0.0-20180801102533+dd5168df1b8a20cb0547a88afd4e4a6cc098e0f1 gitlab.com/athas/fut-gitlab 0.0.0-20180922095419+44bc83247e7b4995dd0c65acf3a72b70d6fe3efe } ================================================ FILE: pkgtests/futhark.pkg.2 ================================================ package github.com/sturluson/testpkg require { github.com/athas/fut-foo 0.1.0 #d285563c25c5152b1ae80fc64de64ff2775fa733 } ================================================ FILE: pkgtests/futhark.pkg.3 ================================================ package github.com/sturluson/testpkg require { github.com/athas/fut-foo 0.1.0 #d285563c25c5152b1ae80fc64de64ff2775fa733 github.com/athas/fut-baz 0.1.0 #6d26e7059eb138f95d4d9747051fc0bb6a4eb85c } ================================================ FILE: pkgtests/futhark.pkg.4 ================================================ package github.com/sturluson/testpkg require { github.com/athas/fut-foo 0.1.0 #d285563c25c5152b1ae80fc64de64ff2775fa733 github.com/athas/fut-baz 0.1.0 #6d26e7059eb138f95d4d9747051fc0bb6a4eb85c } ================================================ FILE: pkgtests/futhark.pkg.5 ================================================ package github.com/sturluson/testpkg require { github.com/athas/fut-foo 0.2.0 #87d372c689131f33bef1b013ac2421fb5e75642b github.com/athas/fut-baz 0.2.0 #44da85224224d37803976c1d30cedb1d2cd20b74 } ================================================ FILE: pkgtests/futhark.pkg.6 ================================================ package github.com/sturluson/testpkg require { github.com/athas/fut-foo 0.2.0 #87d372c689131f33bef1b013ac2421fb5e75642b github.com/athas/fut-baz 0.2.0 #44da85224224d37803976c1d30cedb1d2cd20b74 } ================================================ FILE: pkgtests/futhark.pkg.7 ================================================ package github.com/sturluson/testpkg require { github.com/athas/fut-baz 0.2.0 #44da85224224d37803976c1d30cedb1d2cd20b74 } ================================================ FILE: pkgtests/futhark.pkg.8 ================================================ package github.com/sturluson/testpkg require { github.com/athas/fut-baz 0.2.0 #44da85224224d37803976c1d30cedb1d2cd20b74 } ================================================ FILE: pkgtests/futhark.pkg.9 ================================================ package github.com/sturluson/testpkg require { github.com/athas/fut-baz 0.2.0 #44da85224224d37803976c1d30cedb1d2cd20b74 github.com/athas/fut-foo@2 2.0.0 #9459117fa75aea8fffc677294d25f273e894d19f } ================================================ FILE: pkgtests/lib.10/github.com/athas/fut-bar/bar.fut ================================================ let bar = (0, 1, 0) ================================================ FILE: pkgtests/lib.10/github.com/athas/fut-baz/baz.fut ================================================ module foo_mod = import "../fut-foo/foo" -- Naughty! module bar_mod = import "../fut-bar/bar" -- Naughty! let baz = (0, 1, 1) let foo = foo_mod.foo let bar = bar_mod.bar ================================================ FILE: pkgtests/lib.10/github.com/athas/fut-foo/foo.fut ================================================ let foo = (0, 1, 0) ================================================ FILE: pkgtests/lib.10/github.com/athas/fut-foo@2/foo.fut ================================================ module fut_baz = import "../fut-baz/baz" let foo = (0, 2, 0) let baz = fut_baz.baz ================================================ FILE: pkgtests/lib.11/github.com/athas/fut-bar/bar.fut ================================================ let bar = (0, 1, 0) ================================================ FILE: pkgtests/lib.11/github.com/athas/fut-baz/baz.fut ================================================ module foo_mod = import "../fut-foo/foo" -- Naughty! module bar_mod = import "../fut-bar/bar" -- Naughty! let baz = (0, 1, 1) let foo = foo_mod.foo let bar = bar_mod.bar ================================================ FILE: pkgtests/lib.11/github.com/athas/fut-foo/foo.fut ================================================ let foo = (0, 1, 0) ================================================ FILE: pkgtests/lib.11/github.com/athas/fut-foo@2/foo.fut ================================================ module fut_baz = import "../fut-baz/baz" let foo = (0, 2, 0) let baz = fut_baz.baz ================================================ FILE: pkgtests/lib.12/github.com/athas/fut-bar/bar.fut ================================================ let bar = (0, 1, 0) ================================================ FILE: pkgtests/lib.12/github.com/athas/fut-baz/baz.fut ================================================ module foo_mod = import "../fut-foo/foo" -- Naughty! module bar_mod = import "../fut-bar/bar" -- Naughty! let baz = (0, 1, 1) let foo = foo_mod.foo let bar = bar_mod.bar ================================================ FILE: pkgtests/lib.12/github.com/athas/fut-foo/foo.fut ================================================ let foo = (0, 1, 0) ================================================ FILE: pkgtests/lib.12/github.com/athas/fut-foo@2/foo.fut ================================================ module fut_baz = import "../fut-baz/baz" let foo = (0, 2, 0) let baz = fut_baz.baz ================================================ FILE: pkgtests/lib.12/github.com/athas/fut-quux/quux.fut ================================================ let quux = "123" ================================================ FILE: pkgtests/lib.13/github.com/athas/fut-bar/bar.fut ================================================ let bar = (0, 1, 0) ================================================ FILE: pkgtests/lib.13/github.com/athas/fut-baz/baz.fut ================================================ module foo_mod = import "../fut-foo/foo" -- Naughty! module bar_mod = import "../fut-bar/bar" -- Naughty! let baz = (0, 1, 1) let foo = foo_mod.foo let bar = bar_mod.bar ================================================ FILE: pkgtests/lib.13/github.com/athas/fut-foo/foo.fut ================================================ let foo = (0, 1, 0) ================================================ FILE: pkgtests/lib.13/github.com/athas/fut-foo@2/foo.fut ================================================ module fut_baz = import "../fut-baz/baz" let foo = (0, 2, 0) let baz = fut_baz.baz ================================================ FILE: pkgtests/lib.13/github.com/athas/fut-quux/quux.fut ================================================ let quux = "123" ================================================ FILE: pkgtests/lib.14/github.com/athas/fut-bar/bar.fut ================================================ let bar = (0, 1, 0) ================================================ FILE: pkgtests/lib.14/github.com/athas/fut-baz/baz.fut ================================================ module foo_mod = import "../fut-foo/foo" -- Naughty! module bar_mod = import "../fut-bar/bar" -- Naughty! let baz = (0, 1, 1) let foo = foo_mod.foo let bar = bar_mod.bar ================================================ FILE: pkgtests/lib.14/github.com/athas/fut-foo/foo.fut ================================================ let foo = (0, 1, 0) ================================================ FILE: pkgtests/lib.14/github.com/athas/fut-foo@2/foo.fut ================================================ module fut_baz = import "../fut-baz/baz" let foo = (0, 2, 0) let baz = fut_baz.baz ================================================ FILE: pkgtests/lib.14/github.com/athas/fut-quux/quux.fut ================================================ -- This is not a version number, but this is also not a released -- version! let quux = "123" ================================================ FILE: pkgtests/lib.15/github.com/athas/fut-bar/bar.fut ================================================ let bar = (0, 1, 0) ================================================ FILE: pkgtests/lib.15/github.com/athas/fut-baz/baz.fut ================================================ module foo_mod = import "../fut-foo/foo" -- Naughty! module bar_mod = import "../fut-bar/bar" -- Naughty! let baz = (0, 1, 1) let foo = foo_mod.foo let bar = bar_mod.bar ================================================ FILE: pkgtests/lib.15/github.com/athas/fut-foo/foo.fut ================================================ let foo = (0, 1, 0) ================================================ FILE: pkgtests/lib.15/github.com/athas/fut-foo@2/foo.fut ================================================ module fut_baz = import "../fut-baz/baz" let foo = (0, 2, 0) let baz = fut_baz.baz ================================================ FILE: pkgtests/lib.15/github.com/athas/fut-quux/quux.fut ================================================ -- This is not a version number, but this is also not a released -- version! let quux = "123" ================================================ FILE: pkgtests/lib.16/github.com/athas/fut-bar/bar.fut ================================================ let bar = (0, 1, 0) ================================================ FILE: pkgtests/lib.16/github.com/athas/fut-baz/baz.fut ================================================ module foo_mod = import "../fut-foo/foo" -- Naughty! module bar_mod = import "../fut-bar/bar" -- Naughty! let baz = (0, 1, 1) let foo = foo_mod.foo let bar = bar_mod.bar ================================================ FILE: pkgtests/lib.16/github.com/athas/fut-foo/foo.fut ================================================ let foo = (0, 1, 0) ================================================ FILE: pkgtests/lib.16/github.com/athas/fut-foo@2/foo.fut ================================================ module fut_baz = import "../fut-baz/baz" let foo = (0, 2, 0) let baz = fut_baz.baz ================================================ FILE: pkgtests/lib.16/github.com/athas/fut-quux/quux.fut ================================================ -- This is not a version number, but this is also not a released -- version! let quux = "123" ================================================ FILE: pkgtests/lib.16/gitlab.com/athas/fut-gitlab/gitlab.fut ================================================ let gitlab = (1, 0, 1) ================================================ FILE: pkgtests/lib.17/github.com/athas/fut-bar/bar.fut ================================================ let bar = (0, 1, 0) ================================================ FILE: pkgtests/lib.17/github.com/athas/fut-baz/baz.fut ================================================ module foo_mod = import "../fut-foo/foo" -- Naughty! module bar_mod = import "../fut-bar/bar" -- Naughty! let baz = (0, 1, 1) let foo = foo_mod.foo let bar = bar_mod.bar ================================================ FILE: pkgtests/lib.17/github.com/athas/fut-foo/foo.fut ================================================ let foo = (0, 1, 0) ================================================ FILE: pkgtests/lib.17/github.com/athas/fut-foo@2/foo.fut ================================================ module fut_baz = import "../fut-baz/baz" let foo = (0, 2, 0) let baz = fut_baz.baz ================================================ FILE: pkgtests/lib.17/github.com/athas/fut-quux/quux.fut ================================================ -- This is not a version number, but this is also not a released -- version! let quux = "123" ================================================ FILE: pkgtests/lib.18/github.com/athas/fut-bar/bar.fut ================================================ let bar = (0, 1, 0) ================================================ FILE: pkgtests/lib.18/github.com/athas/fut-baz/baz.fut ================================================ module foo_mod = import "../fut-foo/foo" -- Naughty! module bar_mod = import "../fut-bar/bar" -- Naughty! let baz = (0, 1, 1) let foo = foo_mod.foo let bar = bar_mod.bar ================================================ FILE: pkgtests/lib.18/github.com/athas/fut-foo/foo.fut ================================================ let foo = (0, 1, 0) ================================================ FILE: pkgtests/lib.18/github.com/athas/fut-foo@2/foo.fut ================================================ module fut_baz = import "../fut-baz/baz" let foo = (0, 2, 0) let baz = fut_baz.baz ================================================ FILE: pkgtests/lib.18/github.com/athas/fut-quux/quux.fut ================================================ -- This is not a version number, but this is also not a released -- version! let quux = "123" ================================================ FILE: pkgtests/lib.18/gitlab.com/athas/fut-gitlab/gitlab.fut ================================================ -- Unreleased version. let gitlab = (1, 0, 1) ================================================ FILE: pkgtests/lib.2/github.com/athas/fut-foo/foo.fut ================================================ let foo = (0, 1, 0) ================================================ FILE: pkgtests/lib.3/github.com/athas/fut-foo/foo.fut ================================================ let foo = (0, 1, 0) ================================================ FILE: pkgtests/lib.4/github.com/athas/fut-baz/baz.fut ================================================ module foo_mod = import "../fut-foo/foo" -- Naughty! let baz = (0, 1, 0) let foo = foo_mod.foo ================================================ FILE: pkgtests/lib.4/github.com/athas/fut-foo/foo.fut ================================================ let foo = (0, 1, 0) ================================================ FILE: pkgtests/lib.5/github.com/athas/fut-baz/baz.fut ================================================ module foo_mod = import "../fut-foo/foo" -- Naughty! let baz = (0, 1, 0) let foo = foo_mod.foo ================================================ FILE: pkgtests/lib.5/github.com/athas/fut-foo/foo.fut ================================================ let foo = (0, 1, 0) ================================================ FILE: pkgtests/lib.6/github.com/athas/fut-bar/bar.fut ================================================ let bar = (0, 1, 0) ================================================ FILE: pkgtests/lib.6/github.com/athas/fut-baz/baz.fut ================================================ module foo_mod = import "../fut-foo/foo" -- Naughty! module bar_mod = import "../fut-bar/bar" -- Naughty! let baz = (0, 1, 1) let foo = foo_mod.foo let bar = bar_mod.bar ================================================ FILE: pkgtests/lib.6/github.com/athas/fut-foo/foo.fut ================================================ module fut_bar = import "../../../github.com/athas/fut-bar/bar" let foo = (0, 2, 0) let bar = fut_bar.bar ================================================ FILE: pkgtests/lib.7/github.com/athas/fut-bar/bar.fut ================================================ let bar = (0, 1, 0) ================================================ FILE: pkgtests/lib.7/github.com/athas/fut-baz/baz.fut ================================================ module foo_mod = import "../fut-foo/foo" -- Naughty! module bar_mod = import "../fut-bar/bar" -- Naughty! let baz = (0, 1, 1) let foo = foo_mod.foo let bar = bar_mod.bar ================================================ FILE: pkgtests/lib.7/github.com/athas/fut-foo/foo.fut ================================================ module fut_bar = import "../../../github.com/athas/fut-bar/bar" let foo = (0, 2, 0) let bar = fut_bar.bar ================================================ FILE: pkgtests/lib.8/github.com/athas/fut-bar/bar.fut ================================================ let bar = (0, 1, 0) ================================================ FILE: pkgtests/lib.8/github.com/athas/fut-baz/baz.fut ================================================ module foo_mod = import "../fut-foo/foo" -- Naughty! module bar_mod = import "../fut-bar/bar" -- Naughty! let baz = (0, 1, 1) let foo = foo_mod.foo let bar = bar_mod.bar ================================================ FILE: pkgtests/lib.8/github.com/athas/fut-foo/foo.fut ================================================ let foo = (0, 1, 0) ================================================ FILE: pkgtests/lib.9/github.com/athas/fut-bar/bar.fut ================================================ let bar = (0, 1, 0) ================================================ FILE: pkgtests/lib.9/github.com/athas/fut-baz/baz.fut ================================================ module foo_mod = import "../fut-foo/foo" -- Naughty! module bar_mod = import "../fut-bar/bar" -- Naughty! let baz = (0, 1, 1) let foo = foo_mod.foo let bar = bar_mod.bar ================================================ FILE: pkgtests/lib.9/github.com/athas/fut-foo/foo.fut ================================================ let foo = (0, 1, 0) ================================================ FILE: pkgtests/test.sh ================================================ #!/bin/sh # # You must be in the directory when running this script. It does not # try to be clever. set -e # Die on error. FUTPKG="../src/futpkg" #FUTPKG="../src/futpkg -v" #FUTPKG="futhark pkg" lastrun="" expects () { # Hack to correctly handle empty directories, which are otherwise # not committed to Git. if ! diff -urN $1 $2; then echo "After command '$lastrun', $1 does not match $2" exit 1 fi } i=0 succeed () { lastrun="$@" echo '$' "$@" if ! "$@"; then echo "Command '$lastrun' failed unexpectedly." exit 1 fi expects futhark.pkg futhark.pkg.$i expects lib lib.$i i=$(($i+1)) } fail () { lastrun="$@" echo '$' "$@" if "$@"; then echo "Command '$lastrun' succeeded unexpectedly." exit 1 fi } # Clean up after previous test runs. rm -rf futhark.pkg lib succeed $FUTPKG init github.com/sturluson/testpkg succeed $FUTPKG add github.com/athas/fut-foo 0.1.0 succeed $FUTPKG sync succeed $FUTPKG add github.com/athas/fut-baz 0.1.0 succeed $FUTPKG sync succeed $FUTPKG upgrade succeed $FUTPKG sync succeed $FUTPKG remove github.com/athas/fut-foo succeed $FUTPKG sync succeed $FUTPKG add github.com/athas/fut-foo@2 2.0.0 succeed $FUTPKG sync succeed $FUTPKG add github.com/athas/fut-quux 0.0.0-20180801102532+b70028521e4dbcc286834b32ce82c1d2721a6209 succeed $FUTPKG sync succeed $FUTPKG add github.com/athas/fut-quux 0.0.0-20180801102533+dd5168df1b8a20cb0547a88afd4e4a6cc098e0f1 succeed $FUTPKG sync succeed $FUTPKG add gitlab.com/athas/fut-gitlab succeed $FUTPKG sync ================================================ FILE: src/.gitignore ================================================ MLB *~ smlpkg futpkg version.gen.sml ================================================ FILE: src/Makefile ================================================ PKGVERSION=$(shell cat ../version.txt) MLCOMP ?= mlkit SMLFILES=$(shell find . -name '*.sml') $(shell find . -name '*.sig') $(shell find . -name '*.mlb') GITVERSION=$(shell git describe --abbrev --dirty --always --tags) .PHONY: all all: smlpkg futpkg smlpkg: smlpkg.mlb $(SMLFILES) version.gen.sml $(MLCOMP) -output $@ $< futpkg: futpkg.mlb $(SMLFILES) $(MLCOMP) -output $@ $< .PHONY: test test: $(MAKE) -C semver test $(MAKE) -C manifest test $(MAKE) -C solve test $(MAKE) -C util test .PHONY: remotetest remotetest: $(MAKE) -C test test version.gen.sml: Makefile ../version.txt echo 'structure Version = struct' > $@ echo " val version = \"$(PKGVERSION)\"" >> $@ echo " val gitversion = \"$(GITVERSION)\"" >> $@ echo "end" >> $@ .PHONY: clean clean: rm -rf *~ MLB run pkg.exe smlpkg version.gen.sml $(MAKE) -C semver clean $(MAKE) -C manifest clean $(MAKE) -C solve clean $(MAKE) -C parsecomb clean $(MAKE) -C util clean $(MAKE) -C test clean ================================================ FILE: src/futpkg.mlb ================================================ pkg.mlb futpkg.sml ================================================ FILE: src/futpkg.sml ================================================ val () = Pkg.main "futhark.pkg" ================================================ FILE: src/manifest/.gitignore ================================================ MLB *.exe ================================================ FILE: src/manifest/Makefile ================================================ MLCOMP ?= mlkit .PHONY: test test: $(MLCOMP) -output test.exe test.mlb ./test.exe .PHONY: clean clean: rm -rf *~ MLB run *.exe ================================================ FILE: src/manifest/lex.sig ================================================ signature LEX = sig type reg = Region.reg type loc = Region.loc type filename = string datatype token = Symb of char | Id of string val pr_token : token -> string val lex : filename -> string -> (token * reg) list end ================================================ FILE: src/manifest/lex.sml ================================================ structure Lex :> LEX = struct structure R = Region type reg = R.reg type loc = R.loc type filename = R.filename datatype token = Symb of char | Id of string fun pr_token (Symb c) = "'" ^ String.str c ^ "'" | pr_token (Id s) = "'" ^ s ^ "'" fun loc0 (f:filename) : loc = (1,0,f) (* line 1, char 0 *) datatype state = StartS | IdS of string * loc * loc fun isSymb c = CharVector.exists (fn c' => c'=c) "{}/#" fun lexError (loc:loc) (s:string) : 'a = let val msg = "Lexical error at location " ^ R.ppLoc loc ^ ": " ^ s in raise Fail msg end type lexstate = (token * reg) list * state * loc fun process (c:char,(tokens,state,loc):lexstate) : lexstate = let fun next' c loc = if c = #"\n" then R.newline loc else R.next loc fun proc (tokens,state,loc) = case state of StartS => if Char.isSpace c then (tokens, state, next' c loc) else if isSymb c then ((Symb c,(loc,loc))::tokens, StartS, R.next loc) else if Char.isPrint c then (tokens, IdS(String.str c,loc,loc), R.next loc) else lexError loc "expecting printable character" | IdS(s,l0,l1) => if Char.isSpace c then ((Id s,(l0,l1))::tokens, StartS, next' c loc) else if isSymb c then ((Symb c,(loc,loc))::(Id s,(l0,l1))::tokens, StartS, R.next loc) else if Char.isPrint c then (tokens, IdS(s ^ String.str c,l0,loc), R.next loc) else lexError loc "expecting printable character" in proc(tokens,state,loc) end fun lex (filename: filename) (s:string) : (token * reg) list = let val s = s^" " (* pad some whitespace to keep the lexer happy *) val (tokens,state,_) = CharVector.foldl process (nil,StartS,R.loc0 filename) s in rev tokens end end ================================================ FILE: src/manifest/manifest.mlb ================================================ local $(SML_LIB)/basis/basis.mlb ../parsecomb/parsecomb.mlb lex.sig lex.sml in ../semver/semver.mlb manifest.sig manifest.sml end ================================================ FILE: src/manifest/manifest.sig ================================================ signature MANIFEST = sig type t type pkgpath = {host:string,owner:string,repo:string} type semver = SemVer.t type hash = string type filename = string type required = pkgpath * semver * hash option val pkgpathToString : pkgpath -> string val pkgpathFromString : string -> pkgpath option val package : t -> pkgpath option val requires : t -> required list val toString : t -> string (* for saving *) val fromString : filename -> string -> t (* may raise Fail *) val fromFile : filename -> t (* may raise Fail *) val empty : pkgpath option -> t val add_required : required -> t -> t val del_required : pkgpath -> t -> t val get_required : t -> pkgpath -> required option val replace_requires : t -> required list -> t val pkg_dir : t -> string option (* directory containing package files *) val smlpkg_filename : unit -> string val set_smlpkg_filename : string -> unit val isCommitVersion : semver -> string option (* returns the build id *) val commitVersion : string -> string -> semver option end ================================================ FILE: src/manifest/manifest.sml ================================================ structure Manifest :> MANIFEST = struct structure R = Region structure L = Lex structure P = ParseComb(type token = L.token val pr_token = L.pr_token) (* The standard name of package files ; we allow for this standard to change so that we kan use the package manager as a replacement of futhark pkg - mainly for testing... *) local val filename = ref "sml.pkg" in fun smlpkg_filename () = !filename fun set_smlpkg_filename s = filename := s end type pkgpath = {host:string,owner:string,repo:string} fun pkgpathToString (p:pkgpath) : string = #host p ^ "/" ^ #owner p ^ "/" ^ #repo p fun pkgpathFromString (p:string) : pkgpath option = case String.fields (fn c => c = #"/") p of [host,owner,repo] => SOME {host=host,owner=owner,repo=repo} | _ => NONE type semver = SemVer.t type hash = string type filename = string type reg = R.reg type required = pkgpath * semver * hash option type t = {package: pkgpath option, requires: required list} fun package (m: t) : pkgpath option = #package m fun requires (m: t) : required list = #requires m fun toString (m: t) : string = let fun pr_require (p,v,hopt) = " " ^ pkgpathToString p ^ " " ^ SemVer.toString v ^ (case hopt of SOME h => " #" ^ h | NONE => "") ^ "\n" in (case #package m of SOME p => "package " ^ pkgpathToString p ^ "\n\n" | NONE => "") ^ ("require {\n") ^ (String.concat (map pr_require (#requires m))) ^ ("}\n") end fun empty (p: pkgpath option) : t = {package=p, requires=nil} local open P infix >>> ->> >>- ?? ??? || oo oor in (* p_id : string p *) fun p_id nil = NO (R.botloc, fn () => "expecting identifier but found end-of-file") | p_id ((L.Id id,r)::ts) = OK(id,r,ts) | p_id ((t,r:reg)::_) = NO (#1 r, fn () => ("expecting identifier but found token " ^ L.pr_token t)) (* p_symb : token p *) fun p_symb nil = NO (R.botloc,fn () => "reached end-of-file") | p_symb ((t,r:reg)::ts) = case t of L.Symb _ => OK(t,r,ts) | _ => NO (#1 r, fn () => ("expecting symbol but found token " ^ L.pr_token t)) val p_pkgpath : pkgpath p = ((p_id >>- eat (L.Symb #"/")) >>> (p_id >>- eat (L.Symb #"/")) >>> p_id) oo (fn ((a,b),c) => {host=a,owner=b,repo=c}) val p_hash : string p = eat (L.Symb #"#") ->> p_id fun p_semver nil = NO (R.botloc, fn () => "expecting semantic version but found end-of-file") | p_semver ((L.Id id,r)::ts) = (case SemVer.fromString id of SOME v => OK(v,r,ts) | NONE => NO (#1 r, fn () => "expecting semantic version")) | p_semver ((t,r:reg)::_) = NO (#1 r, fn () => ("expecting semantic version but found token " ^ L.pr_token t)) val p_req : required p = ((p_pkgpath >>> p_semver) oo (fn (a,b) => (a,b,NONE)) ?? p_hash) (fn ((a,b,_),c) => (a,b,SOME c)) val rec p_reqs : required list p = fn ts => ((eat (L.Symb #"}") oo (fn () => nil)) || ((p_req oo (fn a => [a]) ?? p_reqs) (op @)) ) ts val p_require : required list p = eat (L.Id "require") ->> eat (L.Symb #"{") ->> p_reqs val p : t p = ((((eat (L.Id "package") ->> p_pkgpath) oo SOME) >>> p_require) || (p_require oo (fn r => (NONE,r)))) oo (fn (p,rs) => {package=p,requires=rs}) fun parse (ts:(token*reg) list) : t = case p ts of OK (v,_,_) => v | NO (loc,f) => raise Fail ("parse error at location " ^ R.ppLoc loc ^ ": " ^ f()) end fun fromString (filename:string) (content:string) : t = let val ts = L.lex filename content in parse ts end fun readAll (filename:string) : string = (* may raise Fail *) let val is = TextIO.openIn filename in (TextIO.inputAll is handle _ => (TextIO.closeIn is; raise Fail ("failed to read file '" ^ filename ^ "'"))) before TextIO.closeIn is end fun fromFile (filename:string) : t = fromString filename (readAll filename) fun add_required (r:required) (t:t) : t = {package= #package t, requires= #requires t @ [r]} fun del_required (pkgpath:pkgpath) (t:t) : t = {package= #package t, requires= List.filter (fn r => #1 r <> pkgpath) (#requires t)} fun get_required (t:t) (pkgpath:pkgpath) : required option = List.find (fn r => #1 r = pkgpath) (#requires t) fun replace_requires (t:t) (rs:required list) : t = {package= #package t, requires=rs} fun pkg_dir (t:t) : string option = case #package t of SOME p => SOME ("lib/" ^ pkgpathToString p) | NONE => NONE (* Versions of the form (0,0,0)-timestamp+hash are treated specially, as a reference to the commit identified uniquely with 'hash' (typically the Git commit ID). This function detects such versions. *) fun isCommitVersion (v:semver) : string option = case (SemVer.major v, SemVer.minor v, SemVer.patch v) of (0,0,0) => SOME (SemVer.idsToString (SemVer.build v)) | _ => NONE (* @commitVersion timestamp commit@ constructs a commit version. *) fun commitVersion (time:string) (commit:string) : semver option = SemVer.fromString ("0.0.0-" ^ time ^ "+" ^ commit) end ================================================ FILE: src/manifest/test.mlb ================================================ local $(SML_LIB)/basis/basis.mlb manifest.mlb in test.sml end ================================================ FILE: src/manifest/test.sml ================================================ open Manifest fun test s f = (if f() then print ("OK : " ^ s ^ "\n") else print ("ERR: " ^ s ^ "\n")) handle Fail e => print ("EXN: " ^ s ^ " raised Fail \"" ^ e ^ "\"\n") val m = "require {}" val () = test "empty-m" (fn () => null(requires(fromString "str" m))) val mp = "package github.com/owner/repo require {}" val () = test "empty-mp" (fn () => null(requires(fromString "str" mp))) val () = test "empty-mp-host" (fn () => SOME "github.com" = Option.map #host (package(fromString "str" mp))) val () = test "empty-mp-owner" (fn () => SOME "owner" = Option.map #owner (package(fromString "str" mp))) val () = test "empty-mp-repo" (fn () => SOME "repo" = Option.map #repo (package(fromString "str" mp))) val mr1 = "require { github.com/owner/repo 1.2.3 #asdefsde }" val () = test "empty-mr1-len" (fn () => 1 = length(requires(fromString "str" mr1))) val () = test "empty-mr1" (fn () => SOME "asdefsde" = #3(List.hd(requires(fromString "str" mr1)))) val mr2 = "require { github.com/owner/repo 1.2.3 #asdefsde github.com/owner2/repo8 43.3.2-alpha #523424abcd }" val () = test "empty-mr2" (fn () => 2 = length(requires(fromString "str" mr2))) val () = test "empty-mr2-version" (fn () => "1.2.3" = SemVer.toString(#2(List.hd(requires(fromString "str" mr2))))) ================================================ FILE: src/parsecomb/.gitignore ================================================ MLB *.exe ================================================ FILE: src/parsecomb/Makefile ================================================ .PHONY: clean clean: rm -rf MLB run *~ ================================================ FILE: src/parsecomb/PARSE_COMB.sig ================================================ (** Simple parser combinator library that keeps track of position information. *) signature PARSE_COMB = sig type token datatype ('a,'b) either = OK of 'a | NO of 'b type locerr = Region.loc * (unit -> string) type 'a p = (token*Region.reg)list -> ('a * Region.reg * (token*Region.reg)list, locerr) either val >>> : 'a p * 'b p -> ('a*'b)p val ->> : unit p * 'b p -> 'b p val >>- : 'a p * unit p -> 'a p val ?? : 'a p * 'b p -> ('a*'b -> 'a) -> 'a p val ??? : 'a p * 'b p -> ('a*'b*Region.reg -> 'a) -> 'a p val || : 'a p * 'a p -> 'a p val oo : 'a p * ('a -> 'b) -> 'b p val ign : 'a p -> unit p val eat : token -> unit p val oor : 'a p * ('a*Region.reg -> 'b) -> 'b p end (** [token] type of tokens. ['a p] type of parsers that parse values of type 'a. a >>> b Sequence parsers a and b a ->> b Sequence parsers a and b, discard result of a a >>- b Sequence parsers a and b, discard result of b (a ?? b) f parse a and maybe continue with b, if both succeeds, combine with f (a ??? b) f same as ??, but giving region information to f a || b alternatives p oo f fmap f p ign p discard the result of a parser p eat t ts "eat" one token t from list ts p oor f fmap f p, giving region info to f *) ================================================ FILE: src/parsecomb/ParseComb.sml ================================================ functor ParseComb(eqtype token val pr_token : token -> string) : PARSE_COMB = struct type loc = Region.loc type reg = Region.reg type token = token (* keep track of the max location - the longest parse *) datatype ('a,'b) either = OK of 'a | NO of 'b type locerr = loc * (unit -> string) fun maxLocerr (l1:locerr) l2 = if Region.lt (#1 l1) (#1 l2) then l2 else l1 type 'a p = (token*reg)list -> ('a * reg * (token*reg)list, locerr) either infix >>> ->> >>- ?? ??? || oo oor fun p1 >>> p2 = fn ts => case p1 ts of OK(v1,r1,ts) => (case p2 ts of OK(v2,r2,ts) => OK((v1,v2), Region.plus ">>>" r1 r2, ts) | NO l => NO (maxLocerr l (#2 r1,fn()=>""))) | NO l => NO l fun p1 ->> p2 = fn ts => case p1 ts of OK((),r1,ts) => (case p2 ts of OK(v2,r2,ts) => OK(v2, Region.plus "->>" r1 r2, ts) | NO l => NO (maxLocerr l (#2 r1,fn()=>""))) | NO l => NO l fun p1 >>- p2 = fn ts => case p1 ts of OK(v,r1,ts) => (case p2 ts of OK((),r2,ts) => OK(v, Region.plus ">>-" r1 r2, ts) | NO l => NO (maxLocerr l (#2 r1,fn()=>""))) | NO l => NO l fun p1 ?? p2 = fn f => fn ts => case p1 ts of OK(v1,r1,ts) => (case p2 ts of OK(v2,r2,ts) => OK(f(v1,v2), Region.plus "??" r1 r2, ts) | _ => OK(v1,r1,ts)) | NO l => NO l fun p1 ??? p2 = fn f => fn ts => case p1 ts of OK(v1,r1,ts) => (case p2 ts of OK(v2,r2,ts) => let val r = Region.plus "???" r1 r2 in OK(f(v1,v2,r), r, ts) end | NO l => OK(v1,r1,ts)) | NO l => NO l fun p1 || p2 = fn ts => case p1 ts of OK(v,r,ts) => OK(v,r,ts) | NO l1 => case p2 ts of OK(v,r,ts) => OK(v,r,ts) | NO l2 => NO (maxLocerr l1 l2) fun ign p ts = case p ts of OK (_,r,ts) => OK ((),r,ts) | NO l => NO l fun p oo f = fn ts => case p ts of OK(v,r,ts) => OK(f v,r,ts) | NO l => NO l fun p oor f = fn ts => case p ts of OK(v,r,ts) => OK(f(v,r),r,ts) | NO l => NO l fun eat t ts = case ts of nil => NO (Region.botloc,fn() => ("expecting token " ^ pr_token t ^ " but reached end-of-file")) | (t',r:Region.reg)::ts' => if t=t' then OK ((),r,ts') else NO (#1 r,fn()=> ("expecting token " ^ pr_token t ^ " but found token " ^ pr_token t')) end ================================================ FILE: src/parsecomb/REGION.sig ================================================ signature REGION = sig type filename = string type loc = int * int * filename type reg = loc * loc val botloc : loc val loc0 : filename -> loc (* line 1, char 1 *) val newline : loc -> loc val next : loc -> loc val lt : loc -> loc -> bool val wf : reg -> bool val ppLoc : loc -> string val pp : reg -> string val plus : string -> reg -> reg -> reg end (* botloc end of file loc0 f line 1, char 1 of file f newline first char on next line next next char on current line lt a b is location a strictly before location b in the file wf r well formed ppLoc pretty print location pp pretty print region plus merge regions (string only used for error reporting) *) ================================================ FILE: src/parsecomb/Region.sml ================================================ structure Region :> REGION = struct type filename = string type loc = int * int * filename type reg = loc * loc val botloc = (0,0,"") fun loc0 f = (1,0,f) fun newline l = if l = botloc then raise Fail "Region.newline: botloc is not a real location" else (#1 l + 1,0,#3 l) fun next l = if l = botloc then raise Fail "Region.next: botloc is not a real location" else (#1 l, #2 l + 1, #3 l) fun lt (l1:loc) (l2:loc) = if l2 = botloc then false else l1 = botloc orelse #1 l1 < #1 l2 orelse (#1 l1 = #1 l2 andalso #2 l1 < #2 l2) fun wf (r:reg) = #3 (#1 r) <> #3 (#2 r) orelse #1 r = #2 r orelse lt (#1 r) (#2 r) fun ppLoc0 (a,b,_) = Int.toString a ^ "." ^ Int.toString b fun ppLoc (l:loc) = #3 l ^ ":" ^ ppLoc0 l fun pp (a,b) = if a = b then ppLoc a else if #3 a = #3 b then #3 a ^ ":" ^ ppLoc0 a ^ "-" ^ ppLoc0 b else ppLoc a ^ "-" ^ ppLoc b fun plus s r1 r2 = if wf r1 andalso wf r2 andalso (lt (#2 r1) (#1 r2) orelse #3 (#2 r1) <> #3 (#1 r2)) then (#1 r1, #2 r2) else raise Fail ("Region " ^ pp r1 ^ " cannot be merged with region " ^ pp r2 ^ " at " ^ s) end ================================================ FILE: src/parsecomb/parsecomb.mlb ================================================ local $(SML_LIB)/basis/basis.mlb in REGION.sig Region.sml PARSE_COMB.sig ParseComb.sml end ================================================ FILE: src/pkg.mlb ================================================ local $(SML_LIB)/basis/basis.mlb semver/semver.mlb manifest/manifest.mlb util/util.mlb pkginfo.sig pkginfo.sml solve/solve.sig solve/solve.sml version.gen.sml in pkg.sml end ================================================ FILE: src/pkg.sml ================================================ structure Pkg: sig val main: string -> unit end = struct structure Solve = Solve(PkgInfo) (* Some utilities *) fun println s = print (s ^ "\n") fun log s = if ! PkgInfo.verboseFlag then println ("[" ^ s ^ "]") else () fun isPrefixList (nil, _) = true | isPrefixList (x :: xs, y :: ys) = x = y andalso isPrefixList (xs, ys) | isPrefixList _ = false fun isInPkgDir (from_dir: string) (f: string) : bool = isPrefixList (System.splitPath from_dir, System.splitPath f) fun is_in e l = List.exists (fn x => x = e) l structure M = FinMapEq type filepath = string type pkgpath = Manifest.pkgpath type required = Manifest.required type buildlist = Solve.buildlist type semver = SemVer.t infix val op = System. val smlpkg_filename = Manifest.smlpkg_filename (* Installing packages *) fun installInDir (bl: buildlist) (dir: filepath) : unit = let fun install (p, v) = let val info = PkgInfo.lookupPackageRev p v val repo_url = PkgInfo.pkgRevRepoUrl info val refe = PkgInfo.pkgRevRef info val () = log ("cloning " ^ repo_url ^ " @ " ^ refe) val m = PkgInfo.pkgRevGetManifest info (* Compute the directory in the repository that contains the package files. *) val from_dir = case Manifest.pkg_dir m of SOME d => d | NONE => raise Fail (smlpkg_filename () ^ " for " ^ Manifest.pkgpathToString p ^ "-" ^ SemVer.toString v ^ " does not define a package path.") (* The directory in the local file system that will contain the package files. *) val pdir = dir Manifest.pkgpathToString p (* Remove any existing directory for this package. *) val () = System.removePathForcibly pdir (* Get the cached bare repository *) val bare_repo = PkgInfo.getCachedRepo repo_url (* Extract files from the bare repository to a temporary location *) val tmpdir = OS.FileSys.tmpName () ^ "-smlpkg-install" val () = log ("extracting " ^ refe ^ " to temporary directory " ^ tmpdir) val () = if System.doesDirExist tmpdir then () else OS.FileSys.mkDir tmpdir (* Perform extraction, copying, and cleanup, ensuring tmpdir is removed on failure. *) in (* Use git archive to extract the specific ref *) let val archive_cmd = "git --git-dir=" ^ System.shellEscape bare_repo ^ " archive " ^ System.shellEscape refe ^ " | tar -x -C " ^ System.shellEscape tmpdir val (status, out, err) = System.command archive_cmd val () = if OS.Process.isSuccess status then () else raise Fail ("Failed to extract " ^ refe ^ " from " ^ repo_url ^ ": " ^ err) (* Copy the package directory to the target location *) val src = tmpdir from_dir val () = log ("copying " ^ src ^ " to " ^ pdir) val () = System.createDirectoryIfMissing true pdir (* Use cp -r to copy all files *) val copy_cmd = "cp -r " ^ System.shellEscape (src ^ "/.") ^ " " ^ System.shellEscape pdir ^ "/" val (status3, out3, err3) = System.command copy_cmd val () = if OS.Process.isSuccess status3 then () else raise Fail ("Failed to copy package files: " ^ err3) (* Clean up temporary directory on success *) val () = log ("removing temporary directory " ^ tmpdir) val () = System.removePathForcibly tmpdir in () end handle e => ( log ("removing temporary directory " ^ tmpdir) ; System.removePathForcibly tmpdir ; raise e ) end val list = M.toList bl in List.app install list; log (Int.toString (length list) ^ " packages installed") end val (libDir: filepath, libNewDir: filepath, libOldDir: filepath) = ("lib", "lib~new", "lib~old") (* Install the packages listed in the build list in the 'lib' directory of the current working directory. Since we are touching the file system, we are going to be very paranoid. In particular, we want to avoid corrupting the 'lib' directory if something fails along the way. The procedure is as follows: 1) Create a directory 'lib~new'. Delete an existing 'lib~new' if necessary. 2) Populate 'lib~new' based on the build list. 3) Rename 'lib' to 'lib~old'. Delete an existing 'lib~old' if necessary. 4) Rename 'lib~new' to 'lib' 5) If the current package has package path 'p', move 'lib~old/p' to 'lib~new/p'. 6) Delete 'lib~old'. Since POSIX at least guarantees atomic renames, the only place this can fail is between steps 3, 4, and 5. In that case, at least the 'lib~old' will still exist and can be put back by the user. *) fun installBuildList (p: pkgpath option) (bl: buildlist) : unit = let val libdir_exists = System.doesDirExist libDir val () = System.removePathForcibly libNewDir (* 1 *) val () = System.createDirectoryIfMissing false libNewDir val () = installInDir bl libNewDir (* 2 *) val () = if libdir_exists then (* 3 *) ( System.removePathForcibly libOldDir ; System.renameDirectory libDir libOldDir ) else () val () = System.renameDirectory libNewDir libDir (* 4 *) val () = case Option.map Manifest.pkgpathToString p of (* 5 *) SOME pfp => if libdir_exists then let val pkgdir_exists = System.doesDirExist (libOldDir pfp) in if pkgdir_exists then (* Ensure the parent directories exist so that we can move the package directory directly. *) ( System.createDirectoryIfMissing true (System.takeDirectory (libDir pfp)) ; System.renameDirectory (libOldDir pfp) (libDir pfp) ) else () end else () | NONE => () val () = if libdir_exists then (* 6 *) System.removePathForcibly libOldDir else () in () end fun getPkgManifest () : Manifest.t = let val smlpkg = smlpkg_filename () in if System.doesDirExist smlpkg then raise Fail (smlpkg ^ " exists, but it is a directory! What in Odin's beard...") else if System.doesFileExist smlpkg then Manifest.fromFile smlpkg else ( log (smlpkg ^ " not found - pretending it's empty.") ; Manifest.empty NONE ) end fun putPkgManifest (m: Manifest.t) : unit = System.writeFile (smlpkg_filename ()) (Manifest.toString m) (* The Command-line interface *) fun usageMsg s = let val prog = OS.Path.file (CommandLine.name ()) in print ("Usage: " ^ prog ^ " [--version] [--verbose] [--help] " ^ s ^ "\n"); OS.Process.exit (OS.Process.failure) end fun doFmt args = case args of [] => let val smlpkg = smlpkg_filename () val m = Manifest.fromFile smlpkg in System.writeFile smlpkg (Manifest.toString m) end | _ => raise Fail "command 'fmt' expects zero arguments." fun doCheck args = case args of [] => let val m = getPkgManifest () val bl = Solve.solveDeps (Solve.pkgRevDeps m) val () = println "Dependencies chosen:" val () = println (Solve.buildListToString bl) in case Manifest.package m of NONE => () | SOME pkgpath => let val pdir = "lib" Manifest.pkgpathToString pkgpath val pdir_exists = OS.FileSys.isDir pdir in if pdir_exists then () else raise Fail ("the directory " ^ pdir ^ " does not exist.") end end | _ => raise Fail "command 'check' expects zero arguments." fun doSync args = case args of [] => let val m = getPkgManifest () val bl = Solve.solveDeps (Solve.pkgRevDeps m) in installBuildList (Manifest.package m) bl end | _ => raise Fail "command 'sync' expects zero arguments." fun pkgpathParse (s: string) : pkgpath = case Manifest.pkgpathFromString s of SOME p => p | NONE => raise Fail ("invalid package path '" ^ s ^ "'.") fun semverParse (s: string) : semver = case SemVer.fromString s of SOME v => v | NONE => raise Fail ("invalid semantic version '" ^ s ^ "'.") fun doAdd' (p: pkgpath) (v: semver) : unit = let val m = getPkgManifest () (* See if this package (and its dependencies) even exists. We do this by running the solver with the dependencies already in the manifest, plus this new one. The Monoid instance for PkgRevDeps is left-biased, so we are careful to use the new version for this package. *) val () = ignore (Solve.solveDeps (M.add (p, (v, NONE)) (Solve.pkgRevDeps m))) (* We either replace any existing occurence of package 'p', or we add a new one. *) val p_info = PkgInfo.lookupPackageRev p v val hash_opt = case (SemVer.major v, SemVer.minor v, SemVer.patch v) of (* We do not perform hash-pinning for (0,0,0)-versions, because these already embed a specific revision ID into their version number. *) (0, 0, 0) => NONE | _ => SOME (PkgInfo.pkgRevCommit p_info) val req = (p, v, hash_opt) val prev_r = Manifest.get_required m p val m = case prev_r of SOME _ => let val m = Manifest.del_required p m in Manifest.add_required req m end | NONE => Manifest.add_required req m val () = case prev_r of SOME prev_r' => if #2 prev_r' = v then println ("Package already at version " ^ SemVer.toString v ^ "; nothing to do.") else println ("Replaced " ^ Manifest.pkgpathToString p ^ " " ^ SemVer.toString (#2 prev_r') ^ " => " ^ SemVer.toString v ^ ".") | NONE => println ("Added new required package " ^ Manifest.pkgpathToString p ^ " " ^ SemVer.toString v ^ ".") in putPkgManifest m; println ("Remember to run '" ^ OS.Path.file (CommandLine.name ()) ^ " sync'.") end fun doAdd args : unit = case args of [p, v] => doAdd' (pkgpathParse p) (semverParse v) | [p] => doAdd' (pkgpathParse p) (PkgInfo.lookupNewestRev (pkgpathParse p)) | _ => raise Fail "command 'add' expects one or two arguments." fun doRemove args : unit = case args of [p as ps] => let val m = getPkgManifest () val p = pkgpathParse p in case Manifest.get_required m p of SOME r => let val m = Manifest.del_required p m in putPkgManifest m; println ("Removed " ^ ps ^ " " ^ SemVer.toString (#2 r) ^ ".") end | NONE => raise Fail ("no package " ^ ps ^ " found in " ^ smlpkg_filename () ^ ".") end | _ => raise Fail "command 'remove' expects one argument." fun doInit args = case args of [p as ps] => let val smlpkg = smlpkg_filename () val () = log "checking for package file" val () = if System.doesFileExist smlpkg then raise Fail (smlpkg ^ " already exists.") else () val p = pkgpathParse p val () = log "creating directory 'lib'" val () = System.createDirectoryIfMissing true ("lib" ps) val () = println ("Created directory 'lib/" ^ ps ^ "'.") val m = Manifest.empty (SOME p) in putPkgManifest m; println ("Wrote " ^ smlpkg_filename () ^ ".") end | _ => raise Fail "command 'init' expects one argument." fun doUpgrade args : unit = case args of [] => let fun upgrade (req: required) : required = let val v = PkgInfo.lookupNewestRev (#1 req) val h = PkgInfo.pkgRevCommit (PkgInfo.lookupPackageRev (#1 req) v) in if v <> (#2 req) then ( println ("Upgraded " ^ Manifest.pkgpathToString (#1 req) ^ " " ^ SemVer.toString (#2 req) ^ " => " ^ SemVer.toString v ^ ".") ; (#1 req, v, SOME h) ) else req end val m = getPkgManifest () val rs0 = Manifest.requires m val rs = List.map upgrade rs0 val m = Manifest.replace_requires m rs in putPkgManifest m; (if rs = rs0 then println ("Nothing to upgrade.") else println ("Remember to run '" ^ OS.Path.file (CommandLine.name ()) ^ " sync'.")) end | _ => raise Fail "command 'upgrade' expects zero arguments." fun doVersions args : unit = case args of [p] => let val p = pkgpathParse p val pinfo = PkgInfo.lookupPackage p val versions = PkgInfo.pkgVersions pinfo in List.app (println o SemVer.toString) (M.keys versions) end | _ => raise Fail "command 'versions' expects one argument." fun print_prog_version () = let val prog = OS.Path.file (CommandLine.name ()) in println (prog ^ " " ^ Version.version ^ " (" ^ Version.gitversion ^ ")") end fun eatFlags args = case args of arg :: args' => if arg = "-v" orelse arg = "--verbose" then (PkgInfo.verboseFlag := true; eatFlags args') else if arg = "-V" orelse arg = "--version" then (print_prog_version (); OS.Process.exit OS.Process.success) else args | nil => args fun main (pkg_filename: string) : unit = let val () = Manifest.set_smlpkg_filename pkg_filename val smlpkg = smlpkg_filename () val commands = [ ("add", (doAdd, "Add another required package to " ^ smlpkg ^ ".")) , ("check", (doCheck, "Check that " ^ smlpkg ^ " is satisfiable.")) , ("init", (doInit, "Create a new " ^ smlpkg ^ " and a lib/ skeleton.")) , ("fmt", (doFmt, "Reformat " ^ smlpkg ^ ".")) , ("sync", (doSync, "Populate lib/ as specified by " ^ smlpkg ^ ".")) , ( "remove" , (doRemove, "Remove a required package from " ^ smlpkg ^ ".") ) , ("upgrade", (doUpgrade, "Upgrade all packages to newest versions.")) , ("versions", (doVersions, "List available versions for a package.")) ] fun look s l = Option.map #2 (List.find (fn (k, v) => s = k) l) fun doUsage () = let val k = List.foldl Int.max 0 (map (size o #1) commands) + 3 val msg = String.concatWith "\n" ([" ...:", "", "Commands:"] @ map (fn (cmd, (_, desc)) => " " ^ StringCvt.padRight #" " k cmd ^ desc) commands) in usageMsg msg end fun simpleUsage () = usageMsg ("options... <" ^ String.concatWith "|" (map #1 commands) ^ ">") in ( case eatFlags (CommandLine.arguments ()) of [] => doUsage () | cmd :: args => case look cmd commands of SOME (doCmd, doc) => (doCmd args handle Fail s => ( println ("Error: " ^ s) ; PkgInfo.cleanupCache () ; simpleUsage () )) | NONE => doUsage () ; PkgInfo.cleanupCache () ) handle e => (PkgInfo.cleanupCache (); raise e) end end ================================================ FILE: src/pkginfo.sig ================================================ signature PKG_INFO = sig type pkgpath = Manifest.pkgpath type semver = SemVer.t type pkg_revinfo val pkgRevRepoUrl : pkg_revinfo -> string val pkgRevRef : pkg_revinfo -> string val pkgRevCommit : pkg_revinfo -> string val pkgRevGetManifest : pkg_revinfo -> Manifest.t (* cached access *) val pkgRevTime : pkg_revinfo -> Time.time type pkg_info val pkgInfo : pkgpath -> pkg_info (* raw - no caching *) val pkgVersions : pkg_info -> (semver,pkg_revinfo) FinMapEq.t (* Package registry - cached access *) val lookupPackage : pkgpath -> pkg_info val lookupPackageCommit : pkgpath -> string option -> semver * pkg_revinfo val lookupPackageRev : pkgpath -> semver -> pkg_revinfo val lookupNewestRev : pkgpath -> semver (* Cache management *) val getCachedRepo : string -> string (* Get cached repository for URL *) val cleanupCache : unit -> unit (* Clean up temporary cache directory *) val verboseFlag : bool ref end ================================================ FILE: src/pkginfo.sml ================================================ structure PkgInfo :> PKG_INFO = struct structure M = FinMapEq fun println s = print (s ^ "\n") val verboseFlag = ref false fun log s = if !verboseFlag then println ("[" ^ s ^ "]") else () type pkgpath = Manifest.pkgpath type semver = SemVer.t type pkg_revinfo = { pkgRevRepoUrl: string , (* git repository URL *) pkgRevRef: string , (* git ref (tag or commit) *) pkgRevCommit: string , (* commit id for verification *) pkgRevGetManifest: unit -> Manifest.t , pkgRevTime: Time.time } fun pkgRevRepoUrl (r: pkg_revinfo) : string = #pkgRevRepoUrl r fun pkgRevRef (r: pkg_revinfo) : string = #pkgRevRef r fun pkgRevCommit (r: pkg_revinfo) : string = #pkgRevCommit r fun pkgRevGetManifest (r: pkg_revinfo) : Manifest.t = #pkgRevGetManifest r () fun pkgRevTime (r: pkg_revinfo) : Time.time = #pkgRevTime r type pkg_info = { pkgVersions: (semver, pkg_revinfo) M.t , pkgLookupCommit: string option -> pkg_revinfo } fun pkgVersions (pinfo: pkg_info) : (semver, pkg_revinfo) M.t = #pkgVersions pinfo fun lookupPkgRev (v: semver) (pi: pkg_info) : pkg_revinfo option = M.lookup (pkgVersions pi) v fun majorRevOfPkg (p: pkgpath) : pkgpath * int list = let fun mk r = {host = #host p, owner = #owner p, repo = r} in case String.fields (fn c => c = #"@") (#repo p) of [r, v] => (case Int.fromString v of SOME i => if Int.toString i = v then (mk r, [i]) else raise Fail "majorRevOfPkg: expecting integer" | NONE => (mk r, [0, 1])) | _ => (p, [0, 1]) end (* Utilities *) fun gitCmd (opts: string list) : string = (* may raise Fail and print errors on stderr *) let val cmd = String.concatWith " " ("git" :: opts) (* val () = (* Avoid Git asking for credentials. We prefer failure. *) setEnv "GIT_TERMINAL_PROMPT" "0" *) val (status, out, err) = System.command cmd in if OS.Process.isSuccess status then out else ( TextIO.output (TextIO.stdErr, err) ; raise Fail ("Failed to execute git command '" ^ cmd ^ "'") ) end (* Shared temporary directory for all repository clones during this execution *) val cacheDir: string option ref = ref NONE fun getCacheDir () : string = case !cacheDir of SOME dir => dir | NONE => let val dir = OS.FileSys.tmpName () ^ "-smlpkg-cache" val () = if System.doesDirExist dir then () else OS.FileSys.mkDir dir val () = log ("created cache directory " ^ dir) val () = cacheDir := SOME dir in dir end (* Convert repository URL to a safe directory name *) fun repoUrlToDir (repo_url: string) : string = let fun sanitize #"/" = #"-" | sanitize #":" = #"-" | sanitize #"@" = #"-" | sanitize #"." = #"-" | sanitize c = c in String.map sanitize repo_url end (* Clone a git repository to the cache directory and return the path. If the repository already exists in the cache, reuse it. *) fun cloneRepo (repo_url: string) : string = let infix val op = System. val cache_dir = getCacheDir () val repo_name = repoUrlToDir repo_url val repo_dir = cache_dir repo_name in if System.doesDirExist repo_dir then (log ("reusing cached repository " ^ repo_dir); repo_dir) else ( log ("cloning repository " ^ repo_url ^ " to " ^ repo_dir) ; let val cmd = "git clone --bare " ^ System.shellEscape repo_url ^ " " ^ System.shellEscape repo_dir val (status, out, err) = System.command cmd val () = if OS.Process.isSuccess status then () else raise Fail ("Failed to clone " ^ repo_url ^ ": " ^ err) in repo_dir end ) end (* Get the manifest from a git repository at a specific ref *) fun getManifestFromRepo (repo_dir: string) (refe: string) : Manifest.t = let val () = log ("reading manifest from " ^ repo_dir ^ " at " ^ refe) val manifest_file = Manifest.smlpkg_filename () val cmd = "git " ^ "--git-dir=" ^ System.shellEscape repo_dir ^ " show " ^ System.shellEscape (refe ^ ":" ^ manifest_file) val (status, out, err) = System.command cmd val () = if OS.Process.isSuccess status then () else raise Fail ("Failed to read " ^ manifest_file ^ " at " ^ refe ^ ": " ^ err) val path = repo_dir ^ "/" ^ refe ^ "/" ^ manifest_file in Manifest.fromString path out end (* Manifest cache to avoid reading the same manifest multiple times *) val cache = let val m: (string * Manifest.t) list ref = ref nil in fn f => fn repo_dir => fn refe => fn () => let val s = repo_dir ^ "@" ^ refe in case List.find (fn (k, _) => k = s) (!m) of SOME (_, v) => v | NONE => let val v = f repo_dir refe in m := (s, v) :: !m; v end end end (* Create a pkg_revinfo for a specific git ref *) fun mkRevInfo (repo_url: string) (repo_dir: string) (refe: string) (hash: string) : pkg_revinfo = let val mc = cache getManifestFromRepo repo_dir refe val time = Time.now () val () = log ("rev info: " ^ repo_url ^ " @ " ^ refe ^ " (" ^ hash ^ ")") in { pkgRevRepoUrl = repo_url , pkgRevRef = refe , pkgRevCommit = hash , pkgRevGetManifest = mc , pkgRevTime = time } end (* Get package info from a git repository *) fun gitPkgInfo (repo_url: string) (versions: int list) : pkg_info = let val () = log ("retrieving list of tags from " ^ repo_url) (* Escape the repository URL before passing it to shell-based helpers *) val safe_repo_url = System.shellEscape repo_url val remote_lines = gitCmd ["ls-remote", safe_repo_url] val remote_lines = String.tokens (fn c => c = #"\n") remote_lines (* Clone the repository once for reading manifests *) val repo_dir = cloneRepo repo_url fun isHeadRef (l: string) : string option = case String.tokens Char.isSpace l of [hash, "HEAD"] => SOME hash | _ => NONE fun revInfo l : (semver * pkg_revinfo) option = case String.tokens Char.isSpace l of [hash, refe] => (case String.fields (fn s => s = #"/") refe of ["refs", "tags", t] => if String.isPrefix "v" t then (case SemVer.fromString (String.extract (t, 1, NONE)) of SOME v => let val m = SemVer.major v in if List.exists (fn i => i = m) versions then let val pinfo = mkRevInfo repo_url repo_dir t hash in SOME (v, pinfo) end else NONE end | NONE => NONE) else NONE | _ => NONE) | _ => NONE in case List.mapPartial isHeadRef remote_lines of head_ref :: _ => let fun def (opt: string option) : string = Option.getOpt (opt, head_ref) val rev_info = M.fromList_eq (List.mapPartial revInfo remote_lines) fun lookupCommit (r: string option) = mkRevInfo repo_url repo_dir (def r) (def r) in {pkgVersions = rev_info, pkgLookupCommit = lookupCommit} end | _ => raise Fail ("Cannot find HEAD ref for " ^ repo_url) end (* Retrieve information about a package based on its package path. This uses Semantic Import Versioning when interacting with repositories. For example, a package @github.com/user/repo@ will match version 0.* or 1.* tags only, a package @github.com/user/repo/v2@ will match 2.* tags, and so forth.. *) (* Construct repository URL from package path *) fun pkgpathToRepoUrl (p: pkgpath) : string = let val (p', _) = majorRevOfPkg p val base = "https://" ^ #host p ^ "/" ^ #owner p ^ "/" ^ #repo p' in case #host p of "gitlab.com" => base ^ ".git" | _ => base (* Works for github.com and most other git hosts *) end (* Raw access - limited caching *) fun pkgInfo (p: pkgpath) : pkg_info = let val repo_url = pkgpathToRepoUrl p val (_, vs) = majorRevOfPkg p in gitPkgInfo repo_url vs end (* Cached access *) local val registry: (pkgpath, pkg_info) M.t ref = ref (M.empty_eq ()) in fun lookupPackage (p: pkgpath) : pkg_info = case M.lookup (!registry) p of SOME i => i | NONE => let val i = pkgInfo p in registry := M.add (p, i) (!registry); i end fun lookupPackageCommit (p: pkgpath) (refe: string option) : semver * pkg_revinfo = let val pinfo = lookupPackage p val rev_info = #pkgLookupCommit pinfo refe val timestamp = Date.fmt "%Y%m%d%H%M%S" (Date.fromTimeLocal (pkgRevTime rev_info)) val v = case Manifest.commitVersion timestamp (pkgRevCommit rev_info) of NONE => raise Fail "impossible: failed to form valid commit version" | SOME v => v val pinfo' = { pkgLookupCommit = #pkgLookupCommit pinfo , pkgVersions = M.add (v, rev_info) (pkgVersions pinfo) } val () = registry := M.add (p, pinfo') (!registry) in (v, rev_info) end (* Look up information about a specific version of a package. *) fun lookupPackageRev (p: pkgpath) (v: semver) : pkg_revinfo = case Manifest.isCommitVersion v of SOME commit => #2 (lookupPackageCommit p (SOME commit)) | NONE => let val pinfo = lookupPackage p in case lookupPkgRev v pinfo of NONE => let val versions = case M.keys (pkgVersions pinfo) of [] => ("Package " ^ Manifest.pkgpathToString p ^ " has no versions. Invalid package path?") | ks => ("Known versions: " ^ String.concatWith ", " (map SemVer.toString ks)) val (_, vs) = majorRevOfPkg p val major = if List.exists (fn x => x = SemVer.major v) vs then "" else ("\nFor major version " ^ Int.toString (SemVer.major v) ^ ", use package path " ^ Manifest.pkgpathToString p ^ "@" ^ Int.toString (SemVer.major v)) in raise Fail ("package " ^ Manifest.pkgpathToString p ^ " does not have a version " ^ SemVer.toString v ^ ".\n" ^ versions ^ major) end | SOME v' => v' end (* Find the newest version of a package. *) fun lookupNewestRev (p: pkgpath) : semver = let val pinfo = lookupPackage p in case M.keys (pkgVersions pinfo) of [] => ( log ("package " ^ Manifest.pkgpathToString p ^ " has no released versions. Using HEAD.") ; #1 (lookupPackageCommit p NONE) ) | v :: vs => let fun max (v1, v2) = if SemVer.< (v1, v2) then v2 else v1 in log "finding newest version of packages"; List.foldl max v vs (* memo: what about versions of * equal priority - should we use * foldr? *) end end end (* Cache management functions *) fun getCachedRepo (repo_url: string) : string = cloneRepo repo_url fun cleanupCache () : unit = case !cacheDir of NONE => () | SOME dir => ( log ("cleaning up cache directory " ^ dir) ; System.removePathForcibly dir ; cacheDir := NONE ) end ================================================ FILE: src/semver/.gitignore ================================================ MLB *.exe ================================================ FILE: src/semver/Makefile ================================================ MLCOMP ?= mlkit .PHONY: test test: $(MLCOMP) -output test.exe test.mlb ./test.exe .PHONY: clean clean: rm -rf *~ MLB run *.exe ================================================ FILE: src/semver/semver.mlb ================================================ local $(SML_LIB)/basis/basis.mlb in semver.sig semver.sml end ================================================ FILE: src/semver/semver.sig ================================================ (* Semantic versioning; see https://semver.org/ *) signature SEMVER = sig datatype id = NUMID of IntInf.int | ALPHAID of string val idToString : id -> string val idsToString : id list -> string eqtype t val fromString : string -> t option val toString : t -> string val < : t * t -> bool val major : t -> int val minor : t -> int val patch : t -> int val prerel : t -> id list val build : t -> id list end ================================================ FILE: src/semver/semver.sml ================================================ (* Semantic versioning; see https://semver.org/ *) structure SemVer :> SEMVER = struct datatype id = NUMID of IntInf.int | ALPHAID of string type t = {major : int, minor : int, patch : int, prerel : id list, build : id list} fun idToString (NUMID i) = IntInf.toString i | idToString (ALPHAID s) = s fun idsToString ids = String.concatWith "." (map idToString ids) fun toString ({major,minor,patch,prerel,build}:t) : string = Int.toString major ^ "." ^ Int.toString minor ^ "." ^ Int.toString patch ^ (case prerel of nil => "" | ids => "-" ^ idsToString ids) ^ (case build of nil => "" | ids => "+" ^ idsToString ids) fun fromString (s: string) : t option = let fun parseAlphaId s : id = if String.size s > 0 andalso CharVector.all (fn c => c = #"-" orelse Char.isDigit c orelse Char.isLower c orelse Char.isUpper c) s then ALPHAID s else raise Fail ("expecting alpha-id - got " ^ s) fun parseId s : id = case IntInf.fromString s of SOME n => if n > 0 andalso IntInf.toString n = s then NUMID n else parseAlphaId s | NONE => parseAlphaId s fun parseIds (s:string) : id list = if s = "" then nil else map parseId (String.fields (fn c => c = #".") s) val (prebuild : string, build : id list) = case String.fields (fn c => c = #"+") s of [a,b] => (a,parseIds b) | [a] => (a,nil) | _ => raise Fail "expecting at most one +" val (pre : string, prerel : id list) = case String.fields (fn c => c = #"-") prebuild of pre::rest => (pre,parseIds (String.concatWith "-" rest)) | _ => raise Fail "impossible" val (major,minor,patch) = case map Int.fromString (String.fields (fn c => c = #".") pre) of [SOME ma,SOME mi,SOME pa] => if ma >= 0 andalso mi >= 0 andalso pa >= 0 andalso Int.toString ma ^ "." ^ Int.toString mi ^ "." ^ Int.toString pa = pre then (ma,mi,pa) else raise Fail "misformed major.minor.patch specification" | _ => raise Fail "too many dots" in SOME {major=major, minor=minor, patch=patch, prerel=prerel, build=build} end handle Fail _ => NONE fun major (t:t) : int = #major t fun minor (t:t) : int = #minor t fun patch (t:t) : int = #patch t fun prerel (t:t) : id list = #prerel t fun build (t:t) : id list = #build t fun idLt (id1:id, id2:id) : bool = case (id1, id2) of (NUMID id1, NUMID id2) => id1 < id2 | (NUMID _, _) => true | (_, NUMID _) => false | (ALPHAID s1, ALPHAID s2) => s1 < s2 fun idsLt (nil, _) = false | idsLt (_, nil) = true | idsLt (ids1: id list, ids2: id list) : bool = let fun lt (ids1,ids2) = case (ids1,ids2) of (nil, nil) => false | (nil, _) => true | (id1::ids1,id2::ids2) => idLt(id1,id2) orelse (id1 = id2 andalso lt(ids1,ids2)) | _ => false in lt (ids1,ids2) end val op < : t * t -> bool = fn (t1,t2) => major t1 < major t2 orelse (major t1 = major t2 andalso (minor t1 < minor t2 orelse (minor t1 = minor t2 andalso (patch t1 < patch t2 orelse (patch t1 = patch t2 andalso (idsLt(prerel t1, prerel t2))))))) end ================================================ FILE: src/semver/test.mlb ================================================ local $(SML_LIB)/basis/basis.mlb semver.mlb in test.sml end ================================================ FILE: src/semver/test.sml ================================================ open SemVer fun test s b = if b then print ("OK : " ^ s ^ "\n") else print ("ERR: " ^ s ^ "\n") fun testf s f = (if f() then print ("OK : " ^ s ^ "\n") else print ("ERR: " ^ s ^ "\n")) handle Fail e => print ("EXN: " ^ s ^ " raised Fail \"" ^ e ^ "\"\n") | Overflow => print ("EXN: " ^ s ^ " raised Overflow\n") fun test_major_minor_patch s (z,a,b) = let val () = test ("major0" ^ s) (Option.map major (fromString z) = SOME 0) val () = test ("major1" ^ s) (Option.map major (fromString a) = SOME 3) val () = test ("major2" ^ s) (Option.map major (fromString b) = SOME 10) val () = test ("minor0" ^ s) (Option.map minor (fromString z) = SOME 0) val () = test ("minor1" ^ s) (Option.map minor (fromString a) = SOME 2) val () = test ("minor2" ^ s) (Option.map minor (fromString b) = SOME 40) val () = test ("patch0" ^ s) (Option.map patch (fromString z) = SOME 0) val () = test ("patch1" ^ s) (Option.map patch (fromString a) = SOME 1) val () = test ("patch2" ^ s) (Option.map patch (fromString b) = SOME 121) val () = test ("patch0" ^ s) (Option.map patch (fromString z) = SOME 0) val () = test ("patch1" ^ s) (Option.map patch (fromString a) = SOME 1) val () = test ("patch2" ^ s) (Option.map patch (fromString b) = SOME 121) in () end val z = "0.0.0" val a = "3.2.1" val b = "10.40.121" val zp = "0.0.0-a2" val ap = "3.2.1-23.a23" val bp = "10.40.121-yt.43.re-s" val zpb = "0.0.0-a2+dfd34" val apb = "3.2.1-23.a23+23.23.4f" val bpb = "10.40.121-yt.43.re-s+er23.34" val zb = "0.0.0+dfd34" val ab = "3.2.1+23.23.4f" val bb = "10.40.121+er23.34" val () = test_major_minor_patch "" (z,a,b) val () = test_major_minor_patch "p" (zp,ap,bp) val () = test_major_minor_patch "b" (zb,ab,bb) val () = test_major_minor_patch "pb" (zpb,apb,bpb) fun test_toString s (z,a,b) = let val () = test ("toString.z"^s) (Option.map toString (fromString z) = SOME z) val () = test ("toString.a"^s) (Option.map toString (fromString a) = SOME a) val () = test ("toString.b"^s) (Option.map toString (fromString b) = SOME b) in () end val () = test_toString "" (z,a,b) val () = test_toString "p" (zp,ap,bp) val () = test_toString "pb" (zpb,apb,bpb) val () = test_toString "b" (zb,ab,bb) fun test_prerel_empty s (z,a,b) = let val () = test ("prerel0"^s) (Option.map prerel (fromString z) = SOME []) val () = test ("prerel1"^s) (Option.map prerel (fromString a) = SOME []) val () = test ("prerel2"^s) (Option.map prerel (fromString b) = SOME []) in () end val () = test_prerel_empty "" (z,a,b) val () = test_prerel_empty "b" (zb,ab,bb) fun test_prerel s (zp,ap,bp) = let val () = test ("prerel0p" ^ s) (Option.map prerel (fromString zp) = SOME [ALPHAID "a2"]) val () = test ("prerel1p" ^ s) (Option.map prerel (fromString ap) = SOME [NUMID 23,ALPHAID"a23"]) val () = test ("prerel2p" ^ s) (Option.map prerel (fromString bp) = SOME [ALPHAID"yt",NUMID 43,ALPHAID"re-s"]) in () end val () = test_prerel "" (zp,ap,bp) val () = test_prerel "b" (zpb,apb,bpb) fun lt (s1,s2) = case (fromString s1, fromString s2) of (SOME a, SOME b) => a < b | _ => false fun notlt (s1,s2) = case (fromString s1, fromString s2) of (SOME a, SOME b) => not(a < b) | _ => false val () = test "lt_major0" (lt("0.1.0","1.2.0") andalso notlt("1.2.0","0.1.0")) val () = test "lt_major1" (lt("9.1.0","10.2.0") andalso notlt("10.2.0","9.1.0")) val () = test "lt_major2" (notlt("1.2.0","1.2.0")) val () = test "lt_major3" (notlt("20.2.0","3.2.0") andalso lt("3.2.0","20.2.0")) val () = test "lt_minor0" (lt("1.1.0","1.2.0") andalso notlt("1.2.0","1.1.0")) val () = test "lt_minor1" (lt("10.9.0","10.12.0") andalso notlt("10.12.0","10.9.0")) val () = test "lt_minor2" (notlt("1.2.0","1.1.0") andalso lt("1.1.0","1.2.0")) val () = test "lt_minor3" (notlt("2.20.0","2.8.0") andalso lt("2.8.0","2.20.0")) val () = test "lt_patch0" (lt("1.1.0","1.1.2") andalso notlt("1.1.2","1.1.0")) val () = test "lt_patch1" (lt("10.9.2","10.9.12") andalso notlt("10.9.12","10.9.2")) val () = test "lt_patch2" (notlt("1.1.2","1.1.2")) val () = test "lt_patch3" (notlt("10.9.12","10.9.8") andalso lt("10.9.8","10.9.12")) val () = test "lt1" (lt("1.0.0","2.0.0") andalso lt("2.0.0","2.1.0") andalso lt("2.1.0","2.1.1")) val () = test "lt2" (lt("1.0.0-alpha","1.0.0") andalso notlt("1.0.0","1.0.0-alpha")) val () = test "lt3" (lt("1.0.0-alpha","1.0.0-alpha.1") andalso notlt("1.0.0-alpha.1","1.0.0-alpha")) val () = test "lt4" (lt("1.0.0-alpha.1","1.0.0-alpha.beta") andalso notlt("1.0.0-alpha.beta","1.0.0-alpha.1")) val () = test "lt5" (lt("1.0.0-alpha.beta","1.0.0-beta")) val () = test "lt6" (lt("1.0.0-beta","1.0.0-beta.2")) val () = test "lt7" (lt("1.0.0-beta.2","1.0.0-beta.11")) val () = test "lt8" (lt("1.0.0-beta.11","1.0.0-rc.1")) val () = test "lt9" (lt("1.0.0-rc.1","1.0.0")) fun testerr s v = test s (fromString s = NONE) val () = testerr "majorerr1" "0a.1.2" val () = testerr "majorerr2" "a0.1.2" val () = testerr "minorerr1" "0.1a.2" val () = testerr "minorerr2" "0.a1.2" val () = testerr "patcherr1" "0.1.2a" val () = testerr "patcherr2" "0.1.a2" val () = testerr "prerelerr1" "0.1.2-s*" val () = testerr "prerelerr2" "0.1.2-*s" val () = testerr "prerelerr1" "0.1.2-ssd+*l" val () = testerr "prerelerr2" "0.1.2-ssd+s*" val () = testf "ovf" (fn () => let val t = fromString "0.0.0-20180801102532+b70028521e4dbcc286834b32ce82c1d2721a6209" in Option.map major t = SOME 0 end) ================================================ FILE: src/smlpkg.mlb ================================================ pkg.mlb smlpkg.sml ================================================ FILE: src/smlpkg.sml ================================================ val () = Pkg.main "sml.pkg" ================================================ FILE: src/solve/.gitignore ================================================ MLB *.exe ================================================ FILE: src/solve/Makefile ================================================ MLCOMP ?= mlkit .PHONY: test test: $(MLCOMP) -output test.exe test.mlb ./test.exe .PHONY: clean clean: rm -rf *~ MLB run *.exe ================================================ FILE: src/solve/solve.sig ================================================ signature SOLVE = sig type pkgpath = Manifest.pkgpath type semver = SemVer.t type buildlist = (pkgpath,semver) FinMapEq.t type hash = string type pkg_rev_deps = (pkgpath,(semver*hash option)) FinMapEq.t val pkgRevDeps : Manifest.t -> pkg_rev_deps val solveDeps : pkg_rev_deps -> buildlist val buildListToString : buildlist -> string end ================================================ FILE: src/solve/solve.sml ================================================ functor Solve (PI : sig type pkg_revinfo val lookupPackageRev : Manifest.pkgpath -> SemVer.t -> pkg_revinfo val pkgRevGetManifest : pkg_revinfo -> Manifest.t val pkgRevCommit : pkg_revinfo -> string end) : SOLVE = struct structure M = FinMapEq type pkgpath = Manifest.pkgpath type semver = SemVer.t type hash = string type pkg_rev_deps = (pkgpath,(semver*hash option))M.t type buildlist = (pkgpath,semver) M.t fun fail s = raise Fail s fun insert (nil,y) = [y] | insert (x::xs,y) = if x=y then x::xs else x::insert(xs,y) fun merge (xs,nil) = xs | merge (xs,y::ys) = merge(insert(xs,y),ys) fun solve (ps:pkgpath list) (d: pkg_rev_deps) : pkg_rev_deps = case ps of nil => d | p::ps => case M.lookup d p of SOME (v,hopt) => let val m = PI.pkgRevGetManifest (PI.lookupPackageRev p v) val rs = List.filter (fn (p,v,hopt) => case M.lookup d p of SOME (v',hopt') => SemVer.<(v',v) | NONE => true) (Manifest.requires m) val d = List.foldl (fn ((p,v,hopt),d) => M.add (p,(v,hopt)) d) d rs in solve (merge(ps,map #1 rs)) d end | NONE => fail "solve internal error - missing pkgpath in dependency map." fun prune (ps:pkgpath list) (d:pkg_rev_deps) (bl:buildlist) : buildlist = case ps of nil => bl | p::ps => case M.lookup d p of SOME (v,hopt) => let val ri = PI.lookupPackageRev p v val () = case hopt of SOME h => if h = PI.pkgRevCommit ri then () else fail ("Package " ^ Manifest.pkgpathToString p ^ " " ^ SemVer.toString v ^ " has commit hash " ^ PI.pkgRevCommit ri ^ ", but expected " ^ h ^ " from package manifest.") | NONE => () val m = PI.pkgRevGetManifest ri val ps' = List.filter (fn p => case M.lookup bl p of SOME _ => false | NONE => true) (map #1 (Manifest.requires m)) in prune (merge(ps',ps)) d (M.add (p,v) bl) end | NONE => fail "prune internal error - missing pkgpath in dependency map." fun solveDeps (d: pkg_rev_deps) : buildlist = let val roots = M.keys d val d = solve roots d (* gradually solve dependencies *) in prune roots d (M.empty_eq()) (* prune packages that are no longer * in the transitive dependencies *) end fun pkgRevDeps (m:Manifest.t) : pkg_rev_deps = List.foldr (fn ((p,v,h),t) => M.add (p,(v,h)) t) (M.empty_eq()) (Manifest.requires m) fun buildListToString (bl: buildlist) : string = M.toString (Manifest.pkgpathToString, SemVer.toString) bl end ================================================ FILE: src/solve/test.mlb ================================================ local $(SML_LIB)/basis/basis.mlb ../semver/semver.mlb ../manifest/manifest.mlb ../util/util.mlb solve.sig solve.sml in test.sml end ================================================ FILE: src/solve/test.sml ================================================ fun println s = print (s ^ "\n") val () = println "Testing Solve" fun test s f = (if f() then print ("OK : " ^ s ^ "\n") else print ("ERR: " ^ s ^ "\n")) handle Fail e => print ("EXN: " ^ s ^ " raised Fail \"" ^ e ^ "\"\n") fun no_version p v = raise Fail ("cannot find version " ^ v ^ " for package " ^ p ^ ".") (* m_repo10_0_1_X : leaf package with no requirements *) val m_repo10_0_1_0 = Manifest.fromString "m_repo10_0_1_0.pkg" "package github.com/owner1/repo10 require {}" val m_repo10_0_1_1 = Manifest.fromString "m_repo10_0_1_1.pkg" "package github.com/owner1/repo10 require {}" (* m_repo20_0_2_0 : package with repo10_0_1_0 requirement *) val m_repo20_0_2_0 = Manifest.fromString "m_repo20_0_2_0.pkg" "package github.com/owner2/repo20 require { github.com/owner1/repo10 0.1.0 }" (* m_repo20_0_2_1 : package with repo10_0_1_1 requirement *) val m_repo20_0_2_1 = Manifest.fromString "m_repo20_0_2_1.pkg" "package github.com/owner2/repo20 require { github.com/owner1/repo10 0.1.1 }" (* m_repo30_0_3_0 : package with repo20_0_2_0 requirement and repo10_0_1_0 requirement *) val m_repo30_0_3_0 = Manifest.fromString "m_repo30_0_3_0.pkg" "package github.com/owner3/repo30 require { github.com/owner1/repo10 0.1.0 github.com/owner2/repo20 0.2.0 }" (* m_repo30_0_3_1 : package with repo20_0_2_1 requirement and repo10_0_1_0 requirement *) val m_repo30_0_3_1 = Manifest.fromString "m_repo30_0_3_1.pkg" "package github.com/owner3/repo30 require { github.com/owner1/repo10 0.1.0 github.com/owner2/repo20 0.2.1 }" structure PkgInfoMock = struct type pkg_revinfo = Manifest.t * string fun lookupPackageRev p v = case Manifest.pkgpathToString p of p as "github.com/owner1/repo10" => (case SemVer.toString v of "0.1.0" => (m_repo10_0_1_0, "m_repo10_0_1_0") | "0.1.1" => (m_repo10_0_1_1, "m_repo10_0_1_1") | v => no_version p v) | p as "github.com/owner2/repo20" => (case SemVer.toString v of "0.2.0" => (m_repo20_0_2_0, "m_repo20_0_2_0") | "0.2.1" => (m_repo20_0_2_1, "m_repo20_0_2_1") | v => no_version p v) | p as "github.com/owner3/repo30" => (case SemVer.toString v of "0.3.0" => (m_repo30_0_3_0, "m_repo30_0_3_0") | "0.3.1" => (m_repo30_0_3_1, "m_repo30_0_3_1") | v => no_version p v) | p => raise Fail ("no package " ^ p) fun pkgRevGetManifest (pr:pkg_revinfo) : Manifest.t = #1 pr fun pkgRevCommit (pr:pkg_revinfo) : string = #2 pr end structure Solve = Solve (PkgInfoMock) fun solveManifest (m:Manifest.t) : string = let val d = Solve.pkgRevDeps m val bl = Solve.solveDeps d in Solve.buildListToString bl end val () = test "solve1" (fn () => solveManifest m_repo10_0_1_0 = "{}") val () = test "solve2" (fn () => let val res = solveManifest m_repo20_0_2_0 val () = println (" result = " ^ res) in res = "{github.com/owner1/repo10:0.1.0}" end) val () = test "solve3" (fn () => let val res = solveManifest m_repo20_0_2_1 val () = println (" result = " ^ res) in res = "{github.com/owner1/repo10:0.1.1}" end) val () = test "solve4" (fn () => let val res = solveManifest m_repo30_0_3_0 val () = println (" result = " ^ res) in res = "{github.com/owner2/repo20:0.2.0,github.com/owner1/repo10:0.1.0}" end) val () = test "solve5" (fn () => let val res = solveManifest m_repo30_0_3_1 val () = println (" result = " ^ res) in res = "{github.com/owner2/repo20:0.2.1,github.com/owner1/repo10:0.1.1}" end) ================================================ FILE: src/test/.gitignore ================================================ *.out *.res *~ MLB ================================================ FILE: src/test/Makefile ================================================ .PHONY: test test: test1.res test2.res test3.res cat $^ # ------------------------------- # The test cases # ------------------------------- # test1 - result to be compared with futhark.pkg.test1.ok futhark.pkg.test1.out: ../futpkg $(MAKE) smallclean ../futpkg -v init github.com/melsman/test ../futpkg -v add github.com/melsman/segmented 0.1.0 mv futhark.pkg $@ # test2 futhark.pkg.test2.out: ../futpkg $(MAKE) smallclean ../futpkg -v init github.com/melsman/test ../futpkg -v add github.com/melsman/segmented 0.1.0 ../futpkg -v add github.com/athas/fut-baz 0.2.0 ../futpkg -v fmt mv futhark.pkg $@ # test3 futhark.pkg.test3.out: ../futpkg $(MAKE) smallclean ../futpkg -v init github.com/melsman/test ../futpkg -v add github.com/melsman/segmented 0.1.0 ../futpkg -v add github.com/athas/fut-baz 0.2.0 ../futpkg -v remove github.com/melsman/segmented mv futhark.pkg $@ # ------------------------------- # The generic test tooling # ------------------------------- %.res: futhark.pkg.%.out @(diff -aq $< futhark.pkg.$*.ok > /dev/null 2>&1; \ if [ $$? -eq 0 ]; then \ echo "OK: $*" > $@ \ ; else \ if [ -e $<.ok ]; then \ echo "ERR: $* - file $< differs from futhark.pkg.$*.ok"; \ echo "ERR: $* - file $< differs from futhark.pkg.$*.ok" > $@ \ ; else \ echo "ERR: $* - file futhark.pkg.$*.ok does not exist"; \ echo "ERR: $* - file futhark.pkg.$*.ok does not exist" > $@ \ ; fi \ ; exit 1 \ ;fi) ../futpkg: $(MAKE) -C .. all .PHONY: smallclean smallclean: rm -rf *~ lib futhark.pkg sml.pkg .PHONY: clean clean: rm -rf *~ lib futhark.pkg sml.pkg *.res *.out ================================================ FILE: src/test/futhark.pkg.test1.ok ================================================ package github.com/melsman/test require { github.com/melsman/segmented 0.1.0 #2f06da7b048cc45cad89a0829012a63ab639d361 } ================================================ FILE: src/test/futhark.pkg.test2.ok ================================================ package github.com/melsman/test require { github.com/melsman/segmented 0.1.0 #2f06da7b048cc45cad89a0829012a63ab639d361 github.com/athas/fut-baz 0.2.0 #44da85224224d37803976c1d30cedb1d2cd20b74 } ================================================ FILE: src/test/futhark.pkg.test3.ok ================================================ package github.com/melsman/test require { github.com/athas/fut-baz 0.2.0 #44da85224224d37803976c1d30cedb1d2cd20b74 } ================================================ FILE: src/util/.gitignore ================================================ MLB *.exe ================================================ FILE: src/util/Makefile ================================================ MLCOMP ?= mlkit .PHONY: test test: $(MLCOMP) -output test.exe test.mlb ./test.exe .PHONY: clean clean: rm -rf *~ MLB run *.exe ================================================ FILE: src/util/finmapeq.sig ================================================ signature FINMAP_EQ = sig type ('a,'b) t val empty : ('a*'a -> bool) -> ('a,'b)t val empty_eq : unit -> (''a,'b)t val lookup : ('a,'b)t -> 'a -> 'b option val add : 'a*'b -> ('a,'b)t -> ('a,'b)t val fold : ('b*'c -> 'c) -> 'c -> ('a,'b)t -> 'c val Fold : (('a*'b)*'c -> 'c) -> 'c -> ('a,'b)t -> 'c val toString : ('a -> string) * ('b -> string) -> ('a,'b)t -> string val fromList : ('a*'a->bool) -> ('a*'b)list -> ('a,'b)t val fromList_eq : (''a*'b)list -> (''a,'b)t val keys : ('a,'b)t -> 'a list val toList : ('a,'b)t -> ('a*'b)list end ================================================ FILE: src/util/finmapeq.sml ================================================ structure FinMapEq :> FINMAP_EQ = struct type ('a,'b) t = {m: ('a*'b) list, eq: 'a*'a->bool} fun empty (eq: 'a * 'a -> bool) : ('a,'b)t = {m=nil,eq=eq} fun empty_eq () : (''a,'b)t = empty (op =) fun lookup (t: ('a,'b)t) (k:'a) : 'b option = Option.map (#2) (List.find (fn (q,_) => #eq t(k,q)) (#m t)) fun rem (k:'a) (t:('a,'b)t) : ('a,'b)t = {eq= #eq t, m=List.filter (fn (q,_) => not(#eq t(q,k))) (#m t)} fun add (k,v) (t:('a,'b)t) : ('a,'b)t = let val t = rem k t in {eq= #eq t, m= (k,v) :: (#m t)} end fun fold (f : 'b*'c -> 'c) (acc:'c) (t:('a,'b)t) : 'c = List.foldr (fn ((_,v),a) => f(v,a)) acc (#m t) fun Fold (f : ('a*'b)*'c -> 'c) (acc:'c) (t:('a,'b)t) : 'c = List.foldr (fn (p,a) => f(p,a)) acc (#m t) fun toString (pd: 'a -> string, pr :'b -> string) (t:('a,'b)t) :string = "{" ^ String.concatWith "," (Fold (fn ((a,b),c) => (pd a ^ ":" ^ pr b) :: c) nil t) ^ "}" fun fromList (eq:'a*'a->bool) (l:('a*'b)list) : ('a,'b)t = List.foldr (fn ((a,b),m) => add (a,b) m) (empty eq) l fun fromList_eq (l:(''a*'b)list) : (''a,'b)t = fromList (op =) l fun keys (t:('a,'b)t) : 'a list = List.map #1 (#m t) fun toList (t:('a,'b)t) : ('a*'b)list = #m t end ================================================ FILE: src/util/system.sig ================================================ signature SYSTEM = sig type path = string type filepath = path type dirpath = path val readFile : filepath -> string val writeFile : filepath -> string -> unit val readFileBin : filepath -> Word8Vector.vector val writeFileBin : filepath -> Word8Vector.vector -> unit val command : string -> OS.Process.status * string * string val shellEscape : string -> string val splitPath : path -> string list val createDirectoryIfMissing : bool -> dirpath -> unit val hasTrailingPathSeparator : path -> bool val makeRelative : dirpath -> path -> string (* for relative paths only *) val doesFileExist : path -> bool (* also returns true if path is a directory *) val doesDirExist : dirpath -> bool val removePathForcibly : path -> unit val renameDirectory : dirpath -> dirpath -> unit val takeDirectory : path -> dirpath val : dirpath * path -> path end ================================================ FILE: src/util/system.sml ================================================ structure System :> SYSTEM = struct structure FS = OS.FileSys structure P = OS.Process type path = string type filepath = path type dirpath = path (* Text-based file operations *) fun readFile (f:filepath) : string = (* may raise Fail *) let val is = TextIO.openIn f in (TextIO.inputAll is handle _ => (TextIO.closeIn is; raise Fail ("failed to read file '" ^ f ^ "'"))) before TextIO.closeIn is end fun writeFile (f:filepath) (s: string) : unit = let val os = TextIO.openOut f in (TextIO.output(os,s); TextIO.closeOut os) handle X => (TextIO.closeOut os; raise X) end (* Binary file operations *) fun readFileBin (f:filepath) : Word8Vector.vector = (* may raise Fail *) let val is = BinIO.openIn f in (BinIO.inputAll is handle _ => (BinIO.closeIn is; raise Fail ("failed to read file '" ^ f ^ "'"))) before BinIO.closeIn is end fun writeFileBin (f:filepath) (s: Word8Vector.vector) : unit = let val os = BinIO.openOut f in (BinIO.output(os,s); BinIO.closeOut os) handle X => (BinIO.closeOut os; raise X) end (* Command execution *) (* Escape a string for safe use in shell commands *) fun shellEscape (s: string) : string = "'" ^ String.translate (fn #"'" => "'\"'\"'" | c => String.str c) s ^ "'" fun command (cmd: string) : P.status * string * string = let val stdoutFile = FS.tmpName() val stderrFile = FS.tmpName() fun cleanup () = (FS.remove stdoutFile; FS.remove stderrFile) in let val s = P.system(cmd ^ " > " ^ stdoutFile ^ " 2> " ^ stderrFile) val out = readFile stdoutFile val err = readFile stderrFile in (s, out, err) before cleanup() end handle X => (cleanup(); raise X) end (* Operations on files, directories, and paths *) fun splitPath (p:string) : string list = String.fields (fn c => c = #"/") p fun hasTrailingPathSeparator (p:string) : bool = size p > 0 andalso String.sub(p,size p - 1) = #"/" infix fun x y = if x = "" then y else x ^ "/" ^ y fun doesFileExist (p:path) : bool = OS.FileSys.access(p,[]) fun doesDirExist (p:dirpath) : bool = OS.FileSys.access(p,[]) andalso OS.FileSys.isDir p fun createDirectoryIfMissing (also_parents:bool) (p:string) : unit = let fun check d = case d of ".." => raise Fail "no support for '..' in dirs" | "." => raise Fail "no support for '.' in dirs" | "" => raise Fail "no support for '' in dirs" | _ => () val dirs = splitPath p val () = List.app check dirs fun loop pre nil = () | loop pre (x::xs) = let val d = pre x in if doesDirExist d then loop d xs else if null xs orelse also_parents then ( (if doesFileExist d then raise Fail ("cannot create directory " ^ d ^ " as a file exists with that name.") else OS.FileSys.mkDir d) ; loop d xs) else raise Fail ("parent directory " ^ d ^ " of " ^ p ^ " does not exist.") end in loop "" dirs end fun isRelative p = if size p > 0 then String.sub(p,0) <> #"/" else raise Fail "isRelative expects non-empty path" fun isAbsolute p = if size p > 0 then String.sub(p,0) = #"/" else raise Fail "isAbsolute expects non-empty path" fun makeRelative (f:dirpath) (p:path) : string = case (isRelative f, isRelative p) of (true, true) => let fun loop (x::xs,y::ys) = if x = y then loop (xs,ys) else raise Fail ("makeRelative failed as " ^ f ^ " is not a subpath of " ^ p) | loop (nil,ys) = String.concatWith "/" ys | loop _ = raise Fail ("makeRelative failed as " ^ f ^ " is not a subpath of " ^ p) in loop(splitPath f, splitPath p) end | _ => raise Fail "makeRelative assumes relative directories as arguments" fun removePathForcibly (p:path) : unit = if doesDirExist p then let fun loop d = case OS.FileSys.readDir d of SOME p' => ( removePathForcibly (p p') ; loop d ) | NONE => OS.FileSys.closeDir d in loop (OS.FileSys.openDir p) ; OS.FileSys.rmDir p end else if doesFileExist p then OS.FileSys.remove p else () fun renameDirectory (old:dirpath) (new:dirpath) : unit = if doesFileExist new then raise Fail ("cannot rename directory as target directory '" ^ new ^ "' already exists") else if doesDirExist old then OS.FileSys.rename{old=old,new=new} else raise Fail ("'" ^ old ^ "' is not a directory.") fun takeDirectory (p:path) : dirpath = OS.Path.dir p end ================================================ FILE: src/util/test.mlb ================================================ local $(SML_LIB)/basis/basis.mlb util.mlb in test.sml test_system.sml end ================================================ FILE: src/util/test.sml ================================================ fun println s = print (s ^ "\n") val () = println "Testing FinMapEq" fun test s b = println(if b then ("OK : " ^ s) else ("ERR: " ^ s)) open FinMapEq val m : (int,string)t = empty_eq() val m1 = add (5, "five") m val m2 = add (8, "eight") m1 val toS : (int,string)t -> string = toString (Int.toString,fn s=>s) val () = test "empty-toString" (toS m = "{}") val () = test "add1-toString" (toS m1 = "{5:five}") val () = test "add2-toString" (toS m2 = "{8:eight,5:five}") ================================================ FILE: src/util/test_system.sml ================================================ fun println s = print (s ^ "\n") val () = println "Testing System" fun test s b = println(if b then ("OK : " ^ s) else ("ERR: " ^ s)) open System val cmd = "cat testfile.txt" val (status,out,err) = command cmd val () = test "system.ok.out" (out = "Hi there\n") val () = test "system.ok.err" (err = "") val () = test "system.ok.status" (OS.Process.isSuccess status) val cmd_err = "cat doesnotexist.txt" val (status_err,out_err,err_err) = command cmd_err val () = test "system.err.out" (out_err = "") val () = test "system.err.err" (err_err = "cat: doesnotexist.txt: No such file or directory\n") val () = test "system.err.status" (not(OS.Process.isSuccess status_err)) ================================================ FILE: src/util/testfile.txt ================================================ Hi there ================================================ FILE: src/util/util.mlb ================================================ local $(SML_LIB)/basis/basis.mlb in finmapeq.sig finmapeq.sml system.sig system.sml end ================================================ FILE: version.txt ================================================ v0.1.7