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 [](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
* [](https://github.com/diku-dk/futhark-data-sml/actions) [github.com/diku-dk/futhark-data-sml](https://github.com/diku-dk/futhark-data-sml)
* [](https://github.com/diku-dk/futhark-server-sml/actions) [github.com/diku-dk/futhark-server-sml](https://github.com/diku-dk/futhark-server-sml)
* [](https://github.com/diku-dk/sml-aplparse/actions)
[github.com/diku-dk/sml-aplparse](https://github.com/diku-dk/sml-aplparse)
* [](https://github.com/diku-dk/sml-base64/actions)
[github.com/diku-dk/sml-base64](https://github.com/diku-dk/sml-base64)
* [](https://github.com/diku-dk/sml-complex/actions)
[github.com/diku-dk/sml-complex](https://github.com/diku-dk/sml-complex)
* [](https://github.com/diku-dk/sml-cstring/actions)
[github.com/diku-dk/sml-cstring](https://github.com/diku-dk/sml-cstring)
* [](https://github.com/diku-dk/sml-hashtable/actions)
[github.com/diku-dk/sml-hashtable](https://github.com/diku-dk/sml-hashtable)
* [](https://github.com/diku-dk/sml-http/actions)
[github.com/diku-dk/sml-http](https://github.com/diku-dk/sml-http)
* [](https://github.com/diku-dk/sml-json/actions)
[github.com/diku-dk/sml-json](https://github.com/diku-dk/sml-json)
* [](https://github.com/diku-dk/sml-matrix/actions)
[github.com/diku-dk/sml-matrix](https://github.com/diku-dk/sml-matrix)
* [](https://github.com/diku-dk/sml-md5/actions)
[github.com/diku-dk/sml-md5](https://github.com/diku-dk/sml-md5)
* [](https://github.com/diku-dk/sml-getopt/actions)
[github.com/diku-dk/sml-getopt](https://github.com/diku-dk/sml-getopt)
* [](https://github.com/diku-dk/sml-parse/actions)
[github.com/diku-dk/sml-parse](https://github.com/diku-dk/sml-parse)
* [](https://github.com/diku-dk/sml-pickle/actions)
[github.com/diku-dk/sml-pickle](https://github.com/diku-dk/sml-pickle)
* [](https://github.com/diku-dk/sml-pretty/actions)
[github.com/diku-dk/sml-pretty](https://github.com/diku-dk/sml-pretty)
* [](https://github.com/diku-dk/sml-regexp/actions)
[github.com/diku-dk/sml-regexp](https://github.com/diku-dk/sml-regexp)
* [](https://github.com/diku-dk/sml-random/actions)
[github.com/diku-dk/sml-random](https://github.com/diku-dk/sml-random)
* [](https://github.com/diku-dk/sml-server/actions)
[github.com/diku-dk/sml-server](https://github.com/diku-dk/sml-server)
* [](https://github.com/diku-dk/sml-setmap/actions)
[github.com/diku-dk/sml-setmap](https://github.com/diku-dk/sml-setmap)
* [](https://github.com/diku-dk/sml-sort/actions)
[github.com/diku-dk/sml-sort](https://github.com/diku-dk/sml-sort)
* [](https://github.com/diku-dk/sml-sha256/actions)
[github.com/diku-dk/sml-sha256](https://github.com/diku-dk/sml-sha256)
* [](https://github.com/diku-dk/sml-sobol/actions)
[github.com/diku-dk/sml-sobol](https://github.com/diku-dk/sml-sobol)
* [](https://github.com/diku-dk/sml-unicode/actions)
[github.com/diku-dk/sml-unicode](https://github.com/diku-dk/sml-unicode)
* [](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
* [](https://github.com/melsman/mlkit-postgresql/actions)
[github.com/melsman/mlkit-postgresql](https://github.com/melsman/mlkit-postgresql)
* [](https://github.com/melsman/mlkit-ssl-socket/actions)
[github.com/melsman/mlkit-ssl-socket](https://github.com/melsman/mlkit-ssl-socket)
* [](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 <nixpkgs> {},
}:
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"
(["<command> ...:", "", "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
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
Condensed preview — 149 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (117K chars).
[
{
"path": ".github/workflows/main.yml",
"chars": 1265,
"preview": "# This is the smlpkg main workflow for building and testing smlpkg\n# on various architectures. The workflow contains tw"
},
{
"path": ".github/workflows/release.yml",
"chars": 1593,
"preview": "name: Release\n\non:\n push:\n tags:\n - 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10\n\njobs:\n build-test-r"
},
{
"path": ".gitignore",
"chars": 3,
"preview": "bin"
},
{
"path": "LICENSE",
"chars": 1070,
"preview": "MIT License\n\nCopyright (c) 2020 Martin Elsman\n\nPermission is hereby granted, free of charge, to any person obtaining a c"
},
{
"path": "Makefile",
"chars": 1283,
"preview": "prefix ?= .\nINSTALLDIR ?= $(prefix)/bin\nINSTALL ?= install\nOS=$(shell uname -s | tr '[:upper:]' '[:lower:]')\n\n.PHONY: al"
},
{
"path": "README.md",
"chars": 8444,
"preview": "# smlpkg [](https://github.com/diku-dk/smlpkg/actions)\n\nT"
},
{
"path": "default.nix",
"chars": 527,
"preview": "{ pkgs ? import <nixpkgs> {},\n}:\n\npkgs.stdenv.mkDerivation rec {\n name = \"smlpkg\";\n\n src = ./.;\n\n nativeBuildInputs ="
},
{
"path": "pkgtests/.gitignore",
"chars": 16,
"preview": "lib\nfuthark.pkg\n"
},
{
"path": "pkgtests/Makefile",
"chars": 79,
"preview": ".PHONY: test\ntest:\n\t./test.sh\n\n.PHONY: clean\nclean:\n\trm -rf lib futhark.pkg *~\n"
},
{
"path": "pkgtests/README.md",
"chars": 528,
"preview": "# futhark-pkg tests\n\nThis directory contains a shell script (sorry) for testing\nfuthark-pkg. This is done by serially p"
},
{
"path": "pkgtests/futhark.pkg.0",
"chars": 50,
"preview": "package github.com/sturluson/testpkg\n\nrequire {\n}\n"
},
{
"path": "pkgtests/futhark.pkg.1",
"chars": 125,
"preview": "package github.com/sturluson/testpkg\n\nrequire {\n github.com/athas/fut-foo 0.1.0 #d285563c25c5152b1ae80fc64de64ff2775fa7"
},
{
"path": "pkgtests/futhark.pkg.10",
"chars": 202,
"preview": "package github.com/sturluson/testpkg\n\nrequire {\n github.com/athas/fut-baz 0.2.0 #44da85224224d37803976c1d30cedb1d2cd20b"
},
{
"path": "pkgtests/futhark.pkg.11",
"chars": 292,
"preview": "package github.com/sturluson/testpkg\n\nrequire {\n github.com/athas/fut-baz 0.2.0 #44da85224224d37803976c1d30cedb1d2cd20b"
},
{
"path": "pkgtests/futhark.pkg.12",
"chars": 292,
"preview": "package github.com/sturluson/testpkg\n\nrequire {\n github.com/athas/fut-baz 0.2.0 #44da85224224d37803976c1d30cedb1d2cd20b"
},
{
"path": "pkgtests/futhark.pkg.13",
"chars": 292,
"preview": "package github.com/sturluson/testpkg\n\nrequire {\n github.com/athas/fut-baz 0.2.0 #44da85224224d37803976c1d30cedb1d2cd20b"
},
{
"path": "pkgtests/futhark.pkg.14",
"chars": 292,
"preview": "package github.com/sturluson/testpkg\n\nrequire {\n github.com/athas/fut-baz 0.2.0 #44da85224224d37803976c1d30cedb1d2cd20b"
},
{
"path": "pkgtests/futhark.pkg.15",
"chars": 370,
"preview": "package github.com/sturluson/testpkg\n\nrequire {\n github.com/athas/fut-baz 0.2.0 #44da85224224d37803976c1d30cedb1d2cd20b"
},
{
"path": "pkgtests/futhark.pkg.16",
"chars": 370,
"preview": "package github.com/sturluson/testpkg\n\nrequire {\n github.com/athas/fut-baz 0.2.0 #44da85224224d37803976c1d30cedb1d2cd20b"
},
{
"path": "pkgtests/futhark.pkg.17",
"chars": 384,
"preview": "package github.com/sturluson/testpkg\n\nrequire {\n github.com/athas/fut-baz 0.2.0 #44da85224224d37803976c1d30cedb1d2cd20b"
},
{
"path": "pkgtests/futhark.pkg.18",
"chars": 384,
"preview": "package github.com/sturluson/testpkg\n\nrequire {\n github.com/athas/fut-baz 0.2.0 #44da85224224d37803976c1d30cedb1d2cd20b"
},
{
"path": "pkgtests/futhark.pkg.2",
"chars": 125,
"preview": "package github.com/sturluson/testpkg\n\nrequire {\n github.com/athas/fut-foo 0.1.0 #d285563c25c5152b1ae80fc64de64ff2775fa7"
},
{
"path": "pkgtests/futhark.pkg.3",
"chars": 200,
"preview": "package github.com/sturluson/testpkg\n\nrequire {\n github.com/athas/fut-foo 0.1.0 #d285563c25c5152b1ae80fc64de64ff2775fa7"
},
{
"path": "pkgtests/futhark.pkg.4",
"chars": 200,
"preview": "package github.com/sturluson/testpkg\n\nrequire {\n github.com/athas/fut-foo 0.1.0 #d285563c25c5152b1ae80fc64de64ff2775fa7"
},
{
"path": "pkgtests/futhark.pkg.5",
"chars": 200,
"preview": "package github.com/sturluson/testpkg\n\nrequire {\n github.com/athas/fut-foo 0.2.0 #87d372c689131f33bef1b013ac2421fb5e7564"
},
{
"path": "pkgtests/futhark.pkg.6",
"chars": 200,
"preview": "package github.com/sturluson/testpkg\n\nrequire {\n github.com/athas/fut-foo 0.2.0 #87d372c689131f33bef1b013ac2421fb5e7564"
},
{
"path": "pkgtests/futhark.pkg.7",
"chars": 125,
"preview": "package github.com/sturluson/testpkg\n\nrequire {\n github.com/athas/fut-baz 0.2.0 #44da85224224d37803976c1d30cedb1d2cd20b"
},
{
"path": "pkgtests/futhark.pkg.8",
"chars": 125,
"preview": "package github.com/sturluson/testpkg\n\nrequire {\n github.com/athas/fut-baz 0.2.0 #44da85224224d37803976c1d30cedb1d2cd20b"
},
{
"path": "pkgtests/futhark.pkg.9",
"chars": 202,
"preview": "package github.com/sturluson/testpkg\n\nrequire {\n github.com/athas/fut-baz 0.2.0 #44da85224224d37803976c1d30cedb1d2cd20b"
},
{
"path": "pkgtests/lib.10/github.com/athas/fut-bar/bar.fut",
"chars": 20,
"preview": "let bar = (0, 1, 0)\n"
},
{
"path": "pkgtests/lib.10/github.com/athas/fut-baz/baz.fut",
"chars": 171,
"preview": "module foo_mod = import \"../fut-foo/foo\" -- Naughty!\nmodule bar_mod = import \"../fut-bar/bar\" -- Naughty!\n\nlet baz = (0,"
},
{
"path": "pkgtests/lib.10/github.com/athas/fut-foo/foo.fut",
"chars": 20,
"preview": "let foo = (0, 1, 0)\n"
},
{
"path": "pkgtests/lib.10/github.com/athas/fut-foo@2/foo.fut",
"chars": 84,
"preview": "module fut_baz = import \"../fut-baz/baz\"\n\nlet foo = (0, 2, 0)\nlet baz = fut_baz.baz\n"
},
{
"path": "pkgtests/lib.11/github.com/athas/fut-bar/bar.fut",
"chars": 20,
"preview": "let bar = (0, 1, 0)\n"
},
{
"path": "pkgtests/lib.11/github.com/athas/fut-baz/baz.fut",
"chars": 171,
"preview": "module foo_mod = import \"../fut-foo/foo\" -- Naughty!\nmodule bar_mod = import \"../fut-bar/bar\" -- Naughty!\n\nlet baz = (0,"
},
{
"path": "pkgtests/lib.11/github.com/athas/fut-foo/foo.fut",
"chars": 20,
"preview": "let foo = (0, 1, 0)\n"
},
{
"path": "pkgtests/lib.11/github.com/athas/fut-foo@2/foo.fut",
"chars": 84,
"preview": "module fut_baz = import \"../fut-baz/baz\"\n\nlet foo = (0, 2, 0)\nlet baz = fut_baz.baz\n"
},
{
"path": "pkgtests/lib.12/github.com/athas/fut-bar/bar.fut",
"chars": 20,
"preview": "let bar = (0, 1, 0)\n"
},
{
"path": "pkgtests/lib.12/github.com/athas/fut-baz/baz.fut",
"chars": 171,
"preview": "module foo_mod = import \"../fut-foo/foo\" -- Naughty!\nmodule bar_mod = import \"../fut-bar/bar\" -- Naughty!\n\nlet baz = (0,"
},
{
"path": "pkgtests/lib.12/github.com/athas/fut-foo/foo.fut",
"chars": 20,
"preview": "let foo = (0, 1, 0)\n"
},
{
"path": "pkgtests/lib.12/github.com/athas/fut-foo@2/foo.fut",
"chars": 84,
"preview": "module fut_baz = import \"../fut-baz/baz\"\n\nlet foo = (0, 2, 0)\nlet baz = fut_baz.baz\n"
},
{
"path": "pkgtests/lib.12/github.com/athas/fut-quux/quux.fut",
"chars": 17,
"preview": "let quux = \"123\"\n"
},
{
"path": "pkgtests/lib.13/github.com/athas/fut-bar/bar.fut",
"chars": 20,
"preview": "let bar = (0, 1, 0)\n"
},
{
"path": "pkgtests/lib.13/github.com/athas/fut-baz/baz.fut",
"chars": 171,
"preview": "module foo_mod = import \"../fut-foo/foo\" -- Naughty!\nmodule bar_mod = import \"../fut-bar/bar\" -- Naughty!\n\nlet baz = (0,"
},
{
"path": "pkgtests/lib.13/github.com/athas/fut-foo/foo.fut",
"chars": 20,
"preview": "let foo = (0, 1, 0)\n"
},
{
"path": "pkgtests/lib.13/github.com/athas/fut-foo@2/foo.fut",
"chars": 84,
"preview": "module fut_baz = import \"../fut-baz/baz\"\n\nlet foo = (0, 2, 0)\nlet baz = fut_baz.baz\n"
},
{
"path": "pkgtests/lib.13/github.com/athas/fut-quux/quux.fut",
"chars": 17,
"preview": "let quux = \"123\"\n"
},
{
"path": "pkgtests/lib.14/github.com/athas/fut-bar/bar.fut",
"chars": 20,
"preview": "let bar = (0, 1, 0)\n"
},
{
"path": "pkgtests/lib.14/github.com/athas/fut-baz/baz.fut",
"chars": 171,
"preview": "module foo_mod = import \"../fut-foo/foo\" -- Naughty!\nmodule bar_mod = import \"../fut-bar/bar\" -- Naughty!\n\nlet baz = (0,"
},
{
"path": "pkgtests/lib.14/github.com/athas/fut-foo/foo.fut",
"chars": 20,
"preview": "let foo = (0, 1, 0)\n"
},
{
"path": "pkgtests/lib.14/github.com/athas/fut-foo@2/foo.fut",
"chars": 84,
"preview": "module fut_baz = import \"../fut-baz/baz\"\n\nlet foo = (0, 2, 0)\nlet baz = fut_baz.baz\n"
},
{
"path": "pkgtests/lib.14/github.com/athas/fut-quux/quux.fut",
"chars": 94,
"preview": "-- This is not a version number, but this is also not a released\n-- version!\nlet quux = \"123\"\n"
},
{
"path": "pkgtests/lib.15/github.com/athas/fut-bar/bar.fut",
"chars": 20,
"preview": "let bar = (0, 1, 0)\n"
},
{
"path": "pkgtests/lib.15/github.com/athas/fut-baz/baz.fut",
"chars": 171,
"preview": "module foo_mod = import \"../fut-foo/foo\" -- Naughty!\nmodule bar_mod = import \"../fut-bar/bar\" -- Naughty!\n\nlet baz = (0,"
},
{
"path": "pkgtests/lib.15/github.com/athas/fut-foo/foo.fut",
"chars": 20,
"preview": "let foo = (0, 1, 0)\n"
},
{
"path": "pkgtests/lib.15/github.com/athas/fut-foo@2/foo.fut",
"chars": 84,
"preview": "module fut_baz = import \"../fut-baz/baz\"\n\nlet foo = (0, 2, 0)\nlet baz = fut_baz.baz\n"
},
{
"path": "pkgtests/lib.15/github.com/athas/fut-quux/quux.fut",
"chars": 94,
"preview": "-- This is not a version number, but this is also not a released\n-- version!\nlet quux = \"123\"\n"
},
{
"path": "pkgtests/lib.16/github.com/athas/fut-bar/bar.fut",
"chars": 20,
"preview": "let bar = (0, 1, 0)\n"
},
{
"path": "pkgtests/lib.16/github.com/athas/fut-baz/baz.fut",
"chars": 171,
"preview": "module foo_mod = import \"../fut-foo/foo\" -- Naughty!\nmodule bar_mod = import \"../fut-bar/bar\" -- Naughty!\n\nlet baz = (0,"
},
{
"path": "pkgtests/lib.16/github.com/athas/fut-foo/foo.fut",
"chars": 20,
"preview": "let foo = (0, 1, 0)\n"
},
{
"path": "pkgtests/lib.16/github.com/athas/fut-foo@2/foo.fut",
"chars": 84,
"preview": "module fut_baz = import \"../fut-baz/baz\"\n\nlet foo = (0, 2, 0)\nlet baz = fut_baz.baz\n"
},
{
"path": "pkgtests/lib.16/github.com/athas/fut-quux/quux.fut",
"chars": 94,
"preview": "-- This is not a version number, but this is also not a released\n-- version!\nlet quux = \"123\"\n"
},
{
"path": "pkgtests/lib.16/gitlab.com/athas/fut-gitlab/gitlab.fut",
"chars": 23,
"preview": "let gitlab = (1, 0, 1)\n"
},
{
"path": "pkgtests/lib.17/github.com/athas/fut-bar/bar.fut",
"chars": 20,
"preview": "let bar = (0, 1, 0)\n"
},
{
"path": "pkgtests/lib.17/github.com/athas/fut-baz/baz.fut",
"chars": 171,
"preview": "module foo_mod = import \"../fut-foo/foo\" -- Naughty!\nmodule bar_mod = import \"../fut-bar/bar\" -- Naughty!\n\nlet baz = (0,"
},
{
"path": "pkgtests/lib.17/github.com/athas/fut-foo/foo.fut",
"chars": 20,
"preview": "let foo = (0, 1, 0)\n"
},
{
"path": "pkgtests/lib.17/github.com/athas/fut-foo@2/foo.fut",
"chars": 84,
"preview": "module fut_baz = import \"../fut-baz/baz\"\n\nlet foo = (0, 2, 0)\nlet baz = fut_baz.baz\n"
},
{
"path": "pkgtests/lib.17/github.com/athas/fut-quux/quux.fut",
"chars": 94,
"preview": "-- This is not a version number, but this is also not a released\n-- version!\nlet quux = \"123\"\n"
},
{
"path": "pkgtests/lib.18/github.com/athas/fut-bar/bar.fut",
"chars": 20,
"preview": "let bar = (0, 1, 0)\n"
},
{
"path": "pkgtests/lib.18/github.com/athas/fut-baz/baz.fut",
"chars": 171,
"preview": "module foo_mod = import \"../fut-foo/foo\" -- Naughty!\nmodule bar_mod = import \"../fut-bar/bar\" -- Naughty!\n\nlet baz = (0,"
},
{
"path": "pkgtests/lib.18/github.com/athas/fut-foo/foo.fut",
"chars": 20,
"preview": "let foo = (0, 1, 0)\n"
},
{
"path": "pkgtests/lib.18/github.com/athas/fut-foo@2/foo.fut",
"chars": 84,
"preview": "module fut_baz = import \"../fut-baz/baz\"\n\nlet foo = (0, 2, 0)\nlet baz = fut_baz.baz\n"
},
{
"path": "pkgtests/lib.18/github.com/athas/fut-quux/quux.fut",
"chars": 94,
"preview": "-- This is not a version number, but this is also not a released\n-- version!\nlet quux = \"123\"\n"
},
{
"path": "pkgtests/lib.18/gitlab.com/athas/fut-gitlab/gitlab.fut",
"chars": 46,
"preview": "-- Unreleased version.\nlet gitlab = (1, 0, 1)\n"
},
{
"path": "pkgtests/lib.2/github.com/athas/fut-foo/foo.fut",
"chars": 20,
"preview": "let foo = (0, 1, 0)\n"
},
{
"path": "pkgtests/lib.3/github.com/athas/fut-foo/foo.fut",
"chars": 20,
"preview": "let foo = (0, 1, 0)\n"
},
{
"path": "pkgtests/lib.4/github.com/athas/fut-baz/baz.fut",
"chars": 96,
"preview": "module foo_mod = import \"../fut-foo/foo\" -- Naughty!\n\nlet baz = (0, 1, 0)\nlet foo = foo_mod.foo\n"
},
{
"path": "pkgtests/lib.4/github.com/athas/fut-foo/foo.fut",
"chars": 20,
"preview": "let foo = (0, 1, 0)\n"
},
{
"path": "pkgtests/lib.5/github.com/athas/fut-baz/baz.fut",
"chars": 96,
"preview": "module foo_mod = import \"../fut-foo/foo\" -- Naughty!\n\nlet baz = (0, 1, 0)\nlet foo = foo_mod.foo\n"
},
{
"path": "pkgtests/lib.5/github.com/athas/fut-foo/foo.fut",
"chars": 20,
"preview": "let foo = (0, 1, 0)\n"
},
{
"path": "pkgtests/lib.6/github.com/athas/fut-bar/bar.fut",
"chars": 20,
"preview": "let bar = (0, 1, 0)\n"
},
{
"path": "pkgtests/lib.6/github.com/athas/fut-baz/baz.fut",
"chars": 171,
"preview": "module foo_mod = import \"../fut-foo/foo\" -- Naughty!\nmodule bar_mod = import \"../fut-bar/bar\" -- Naughty!\n\nlet baz = (0,"
},
{
"path": "pkgtests/lib.6/github.com/athas/fut-foo/foo.fut",
"chars": 107,
"preview": "module fut_bar = import \"../../../github.com/athas/fut-bar/bar\"\n\nlet foo = (0, 2, 0)\nlet bar = fut_bar.bar\n"
},
{
"path": "pkgtests/lib.7/github.com/athas/fut-bar/bar.fut",
"chars": 20,
"preview": "let bar = (0, 1, 0)\n"
},
{
"path": "pkgtests/lib.7/github.com/athas/fut-baz/baz.fut",
"chars": 171,
"preview": "module foo_mod = import \"../fut-foo/foo\" -- Naughty!\nmodule bar_mod = import \"../fut-bar/bar\" -- Naughty!\n\nlet baz = (0,"
},
{
"path": "pkgtests/lib.7/github.com/athas/fut-foo/foo.fut",
"chars": 107,
"preview": "module fut_bar = import \"../../../github.com/athas/fut-bar/bar\"\n\nlet foo = (0, 2, 0)\nlet bar = fut_bar.bar\n"
},
{
"path": "pkgtests/lib.8/github.com/athas/fut-bar/bar.fut",
"chars": 20,
"preview": "let bar = (0, 1, 0)\n"
},
{
"path": "pkgtests/lib.8/github.com/athas/fut-baz/baz.fut",
"chars": 171,
"preview": "module foo_mod = import \"../fut-foo/foo\" -- Naughty!\nmodule bar_mod = import \"../fut-bar/bar\" -- Naughty!\n\nlet baz = (0,"
},
{
"path": "pkgtests/lib.8/github.com/athas/fut-foo/foo.fut",
"chars": 20,
"preview": "let foo = (0, 1, 0)\n"
},
{
"path": "pkgtests/lib.9/github.com/athas/fut-bar/bar.fut",
"chars": 20,
"preview": "let bar = (0, 1, 0)\n"
},
{
"path": "pkgtests/lib.9/github.com/athas/fut-baz/baz.fut",
"chars": 171,
"preview": "module foo_mod = import \"../fut-foo/foo\" -- Naughty!\nmodule bar_mod = import \"../fut-bar/bar\" -- Naughty!\n\nlet baz = (0,"
},
{
"path": "pkgtests/lib.9/github.com/athas/fut-foo/foo.fut",
"chars": 20,
"preview": "let foo = (0, 1, 0)\n"
},
{
"path": "pkgtests/test.sh",
"chars": 1604,
"preview": "#!/bin/sh\n#\n# You must be in the directory when running this script. It does not\n# try to be clever.\n\nset -e # Die on e"
},
{
"path": "src/.gitignore",
"chars": 36,
"preview": "MLB\n*~\nsmlpkg\nfutpkg\nversion.gen.sml"
},
{
"path": "src/Makefile",
"chars": 975,
"preview": "PKGVERSION=$(shell cat ../version.txt)\n\nMLCOMP ?= mlkit\n\nSMLFILES=$(shell find . -name '*.sml') $(shell find . -name '*."
},
{
"path": "src/futpkg.mlb",
"chars": 19,
"preview": "pkg.mlb\nfutpkg.sml\n"
},
{
"path": "src/futpkg.sml",
"chars": 33,
"preview": "\nval () = Pkg.main \"futhark.pkg\"\n"
},
{
"path": "src/manifest/.gitignore",
"chars": 9,
"preview": "MLB\n*.exe"
},
{
"path": "src/manifest/Makefile",
"chars": 132,
"preview": "MLCOMP ?= mlkit\n\n.PHONY: test\ntest:\n\t$(MLCOMP) -output test.exe test.mlb\n\t./test.exe\n\n.PHONY: clean\nclean:\n\trm -rf *~ ML"
},
{
"path": "src/manifest/lex.sig",
"chars": 250,
"preview": "signature LEX = sig\n type reg = Region.reg\n type loc = Region.loc\n type filename = string\n\n datatype token = Symb of"
},
{
"path": "src/manifest/lex.sml",
"chars": 1850,
"preview": "structure Lex :> LEX =\nstruct\nstructure R = Region\ntype reg = R.reg\ntype loc = R.loc\ntype filename = R.filename\ndatatype"
},
{
"path": "src/manifest/manifest.mlb",
"chars": 144,
"preview": "local\n $(SML_LIB)/basis/basis.mlb\n ../parsecomb/parsecomb.mlb\n lex.sig\n lex.sml\nin\n ../semver/semver.mlb\n manifest"
},
{
"path": "src/manifest/manifest.sig",
"chars": 1261,
"preview": "signature MANIFEST = sig\n type t\n type pkgpath = {host:string,owner:string,repo:string}\n type semver = SemVer.t\n typ"
},
{
"path": "src/manifest/manifest.sml",
"chars": 5237,
"preview": "structure Manifest :> MANIFEST = struct\n\nstructure R = Region\nstructure L = Lex\nstructure P = ParseComb(type token = L.t"
},
{
"path": "src/manifest/test.mlb",
"chars": 68,
"preview": "local $(SML_LIB)/basis/basis.mlb\n manifest.mlb\nin test.sml\nend\n"
},
{
"path": "src/manifest/test.sml",
"chars": 1277,
"preview": "\nopen Manifest\n\nfun test s f =\n (if f() then print (\"OK : \" ^ s ^ \"\\n\")\n else print (\"ERR: \" ^ s ^ \"\\n\"))\n han"
},
{
"path": "src/parsecomb/.gitignore",
"chars": 9,
"preview": "MLB\n*.exe"
},
{
"path": "src/parsecomb/Makefile",
"chars": 40,
"preview": ".PHONY: clean\nclean:\n\trm -rf MLB run *~\n"
},
{
"path": "src/parsecomb/PARSE_COMB.sig",
"chars": 1282,
"preview": "(** Simple parser combinator library that keeps track of position information. *)\nsignature PARSE_COMB = sig\n type toke"
},
{
"path": "src/parsecomb/ParseComb.sml",
"chars": 2609,
"preview": "functor ParseComb(eqtype token\n val pr_token : token -> string) : PARSE_COMB = struct\ntype loc = Region"
},
{
"path": "src/parsecomb/REGION.sig",
"chars": 787,
"preview": "signature REGION = sig\n type filename = string\n type loc = int * int * filename\n type reg = loc * loc\n\n val botloc "
},
{
"path": "src/parsecomb/Region.sml",
"chars": 1198,
"preview": "structure Region :> REGION = struct\n type filename = string\n type loc = int * int * filename\n type reg = loc * loc\n "
},
{
"path": "src/parsecomb/parsecomb.mlb",
"chars": 100,
"preview": "local $(SML_LIB)/basis/basis.mlb\nin REGION.sig\n Region.sml\n PARSE_COMB.sig\n ParseComb.sml\nend\n"
},
{
"path": "src/pkg.mlb",
"chars": 228,
"preview": "local $(SML_LIB)/basis/basis.mlb\n semver/semver.mlb\n manifest/manifest.mlb\n util/util.mlb\n pkginfo.s"
},
{
"path": "src/pkg.sml",
"chars": 16681,
"preview": "structure Pkg:\nsig\n val main: string -> unit\nend =\nstruct\n\n structure Solve = Solve(PkgInfo)\n\n (* Some utilities *)\n "
},
{
"path": "src/pkginfo.sig",
"chars": 1064,
"preview": "signature PKG_INFO = sig\n\n type pkgpath = Manifest.pkgpath\n type semver = SemVer.t\n\n type pkg_revinfo\n val pkgRevRep"
},
{
"path": "src/pkginfo.sml",
"chars": 12653,
"preview": "structure PkgInfo :> PKG_INFO =\nstruct\n\n structure M = FinMapEq\n\n fun println s =\n print (s ^ \"\\n\")\n\n val verboseF"
},
{
"path": "src/semver/.gitignore",
"chars": 9,
"preview": "MLB\n*.exe"
},
{
"path": "src/semver/Makefile",
"chars": 132,
"preview": "MLCOMP ?= mlkit\n\n.PHONY: test\ntest:\n\t$(MLCOMP) -output test.exe test.mlb\n\t./test.exe\n\n.PHONY: clean\nclean:\n\trm -rf *~ ML"
},
{
"path": "src/semver/semver.mlb",
"chars": 65,
"preview": "local $(SML_LIB)/basis/basis.mlb\nin semver.sig\n semver.sml\nend\n"
},
{
"path": "src/semver/semver.sig",
"chars": 436,
"preview": "(* Semantic versioning; see https://semver.org/ *)\n\nsignature SEMVER = sig\n\n datatype id = NUMID of IntInf.int | ALPHAI"
},
{
"path": "src/semver/semver.sml",
"chars": 3653,
"preview": "(* Semantic versioning; see https://semver.org/ *)\n\nstructure SemVer :> SEMVER = struct\n\ndatatype id = NUMID of IntInf.i"
},
{
"path": "src/semver/test.mlb",
"chars": 66,
"preview": "local $(SML_LIB)/basis/basis.mlb\n semver.mlb\nin test.sml\nend\n"
},
{
"path": "src/semver/test.sml",
"chars": 5680,
"preview": "\nopen SemVer\n\nfun test s b =\n if b then print (\"OK : \" ^ s ^ \"\\n\")\n else print (\"ERR: \" ^ s ^ \"\\n\")\n\nfun testf s f"
},
{
"path": "src/smlpkg.mlb",
"chars": 19,
"preview": "pkg.mlb\nsmlpkg.sml\n"
},
{
"path": "src/smlpkg.sml",
"chars": 29,
"preview": "\nval () = Pkg.main \"sml.pkg\"\n"
},
{
"path": "src/solve/.gitignore",
"chars": 9,
"preview": "MLB\n*.exe"
},
{
"path": "src/solve/Makefile",
"chars": 132,
"preview": "MLCOMP ?= mlkit\n\n.PHONY: test\ntest:\n\t$(MLCOMP) -output test.exe test.mlb\n\t./test.exe\n\n.PHONY: clean\nclean:\n\trm -rf *~ ML"
},
{
"path": "src/solve/solve.sig",
"chars": 358,
"preview": "signature SOLVE = sig\n\n type pkgpath = Manifest.pkgpath\n type semver = SemVer.t\n type buildlist = (pkgpath,semver) Fi"
},
{
"path": "src/solve/solve.sml",
"chars": 3303,
"preview": "functor Solve (PI :\n sig\n type pkg_revinfo\n val lookupPackageRev : Manife"
},
{
"path": "src/solve/test.mlb",
"chars": 162,
"preview": "local $(SML_LIB)/basis/basis.mlb\n ../semver/semver.mlb\n ../manifest/manifest.mlb\n ../util/util.mlb\n "
},
{
"path": "src/solve/test.sml",
"chars": 4134,
"preview": "\nfun println s = print (s ^ \"\\n\")\n\nval () = println \"Testing Solve\"\n\nfun test s f =\n (if f() then print (\"OK : \" ^ s "
},
{
"path": "src/test/.gitignore",
"chars": 18,
"preview": "*.out\n*.res\n*~\nMLB"
},
{
"path": "src/test/Makefile",
"chars": 1743,
"preview": "\n.PHONY: test\ntest: test1.res test2.res test3.res\n\tcat $^\n\n# -------------------------------\n# The test cases\n# --------"
},
{
"path": "src/test/futhark.pkg.test1.ok",
"chars": 124,
"preview": "package github.com/melsman/test\n\nrequire {\n github.com/melsman/segmented 0.1.0 #2f06da7b048cc45cad89a0829012a63ab639d36"
},
{
"path": "src/test/futhark.pkg.test2.ok",
"chars": 199,
"preview": "package github.com/melsman/test\n\nrequire {\n github.com/melsman/segmented 0.1.0 #2f06da7b048cc45cad89a0829012a63ab639d36"
},
{
"path": "src/test/futhark.pkg.test3.ok",
"chars": 120,
"preview": "package github.com/melsman/test\n\nrequire {\n github.com/athas/fut-baz 0.2.0 #44da85224224d37803976c1d30cedb1d2cd20b74\n}\n"
},
{
"path": "src/util/.gitignore",
"chars": 9,
"preview": "MLB\n*.exe"
},
{
"path": "src/util/Makefile",
"chars": 132,
"preview": "MLCOMP ?= mlkit\n\n.PHONY: test\ntest:\n\t$(MLCOMP) -output test.exe test.mlb\n\t./test.exe\n\n.PHONY: clean\nclean:\n\trm -rf *~ ML"
},
{
"path": "src/util/finmapeq.sig",
"chars": 590,
"preview": "signature FINMAP_EQ = sig\n type ('a,'b) t\n val empty : ('a*'a -> bool) -> ('a,'b)t\n val empty_eq : unit -> (''a,'b"
},
{
"path": "src/util/finmapeq.sml",
"chars": 1271,
"preview": "structure FinMapEq :> FINMAP_EQ = struct\n type ('a,'b) t = {m: ('a*'b) list, eq: 'a*'a->bool}\n\n fun empty (eq: 'a * 'a"
},
{
"path": "src/util/system.sig",
"chars": 1095,
"preview": "\nsignature SYSTEM = sig\n\n type path = string\n type filepath = path\n type dirpath = path\n\n val readFile "
},
{
"path": "src/util/system.sml",
"chars": 4931,
"preview": "structure System :> SYSTEM = struct\n\nstructure FS = OS.FileSys\nstructure P = OS.Process\n\ntype path = string\ntype filepat"
},
{
"path": "src/util/test.mlb",
"chars": 83,
"preview": "local $(SML_LIB)/basis/basis.mlb\n util.mlb\nin test.sml\n test_system.sml\nend\n"
},
{
"path": "src/util/test.sml",
"chars": 490,
"preview": "fun println s = print (s ^ \"\\n\")\n\nval () = println \"Testing FinMapEq\"\n\nfun test s b =\n println(if b then (\"OK : \" ^ s"
},
{
"path": "src/util/test_system.sml",
"chars": 691,
"preview": "fun println s = print (s ^ \"\\n\")\n\nval () = println \"Testing System\"\n\nfun test s b =\n println(if b then (\"OK : \" ^ s)\n"
},
{
"path": "src/util/testfile.txt",
"chars": 9,
"preview": "Hi there\n"
},
{
"path": "src/util/util.mlb",
"chars": 97,
"preview": "local $(SML_LIB)/basis/basis.mlb\nin finmapeq.sig\n finmapeq.sml\n system.sig\n system.sml\nend\n"
},
{
"path": "version.txt",
"chars": 7,
"preview": "v0.1.7\n"
}
]
About this extraction
This page contains the full source code of the diku-dk/smlpkg GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 149 files (100.7 KB), approximately 37.3k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.