├── .envrc ├── example ├── .gitignore ├── bakage.pl ├── local_package │ ├── local.pl │ └── scryer-manifest.pl ├── justfile ├── main.pl └── scryer-manifest.pl ├── tests ├── snapshot │ ├── cases │ │ ├── cli_help.status │ │ ├── cli_help.stderr │ │ ├── subdirectory_imports.stderr │ │ ├── subdirectory_imports.status │ │ ├── subdirectory_imports.in │ │ │ ├── my_module.pl │ │ │ ├── scryer-manifest.pl │ │ │ └── tests │ │ │ │ └── test_my_module.pl │ │ ├── subdirectory_imports.sh │ │ ├── subdirectory_imports.stdout │ │ ├── cli_help.sh │ │ └── cli_help.stdout │ ├── .gitignore │ ├── justfile │ └── snapshot.sh ├── integration │ ├── use_module │ │ ├── .gitignore │ │ ├── bakage.pl │ │ ├── local_package │ │ │ ├── local.pl │ │ │ └── scryer-manifest.pl │ │ ├── justfile │ │ ├── main.pl │ │ └── scryer-manifest.pl │ ├── manifest_with_git_dependency │ │ ├── .gitignore │ │ ├── bakage.pl │ │ ├── scryer-manifest.pl │ │ ├── main.pl │ │ └── justfile │ ├── invalid_manifest_missing_license │ │ ├── .gitignore │ │ ├── bakage.pl │ │ ├── scryer-manifest.pl │ │ ├── justfile │ │ └── main.pl │ ├── invalid_manifest_missing_name │ │ ├── .gitignore │ │ ├── bakage.pl │ │ ├── scryer-manifest.pl │ │ ├── main.pl │ │ └── justfile │ ├── invalid_manifest_multiple_name │ │ ├── .gitignore │ │ ├── bakage.pl │ │ ├── scryer-manifest.pl │ │ ├── main.pl │ │ └── justfile │ ├── manifest_multiple_dependencies │ │ ├── .gitignore │ │ ├── bakage.pl │ │ ├── local_package │ │ │ ├── local.pl │ │ │ └── scryer-manifest.pl │ │ ├── scryer-manifest.pl │ │ ├── main.pl │ │ └── justfile │ ├── manifest_with_git_hash_dependency │ │ ├── .gitignore │ │ ├── license │ │ ├── bakage.pl │ │ ├── scryer-manifest.pl │ │ ├── main.pl │ │ └── justfile │ ├── manifest_with_git_tag_dependency │ │ ├── .gitignore │ │ ├── bakage.pl │ │ ├── scryer-manifest.pl │ │ ├── main.pl │ │ └── justfile │ ├── manifest_with_no_dependencies │ │ ├── .gitignore │ │ ├── bakage.pl │ │ ├── scryer-manifest.pl │ │ ├── main.pl │ │ └── justfile │ ├── manifest_with_path_dependency │ │ ├── .gitignore │ │ ├── bakage.pl │ │ ├── local_package │ │ │ ├── local.pl │ │ │ └── scryer-manifest.pl │ │ ├── scryer-manifest.pl │ │ ├── main.pl │ │ └── justfile │ ├── invalid_manifest_invalid_license_path │ │ ├── .gitignore │ │ ├── bakage.pl │ │ ├── scryer-manifest.pl │ │ ├── main.pl │ │ └── justfile │ ├── invalid_manifest_multiple_licenses │ │ ├── .gitignore │ │ ├── bakage.pl │ │ ├── scryer-manifest.pl │ │ ├── main.pl │ │ └── justfile │ ├── invalid_manifest_multiple_main_file │ │ ├── .gitignore │ │ ├── bakage.pl │ │ ├── scryer-manifest.pl │ │ ├── main.pl │ │ └── justfile │ ├── manifest_with_git_branch_dependency │ │ ├── .gitignore │ │ ├── bakage.pl │ │ ├── scryer-manifest.pl │ │ ├── main.pl │ │ └── justfile │ ├── manifest_with_no_dependency_predicate │ │ ├── .gitignore │ │ ├── bakage.pl │ │ ├── scryer-manifest.pl │ │ ├── main.pl │ │ └── justfile │ ├── all_invalid_dependencies_stop_installation │ │ ├── .gitignore │ │ ├── bakage.pl │ │ ├── local_package │ │ │ ├── local.pl │ │ │ └── scryer-manifest.pl │ │ ├── justfile │ │ ├── scryer-manifest.pl │ │ └── main.pl │ ├── one_invalid_dependency_stop_installation │ │ ├── .gitignore │ │ ├── bakage.pl │ │ ├── local_package │ │ │ ├── local.pl │ │ │ └── scryer-manifest.pl │ │ ├── justfile │ │ ├── scryer-manifest.pl │ │ └── main.pl │ ├── manifest_multiple_dependencies_with_all_errors │ │ ├── .gitignore │ │ ├── bakage.pl │ │ ├── local_package │ │ │ ├── local.pl │ │ │ └── scryer-manifest.pl │ │ ├── justfile │ │ ├── scryer-manifest.pl │ │ └── main.pl │ ├── manifest_multiple_dependencies_with_one_error │ │ ├── .gitignore │ │ ├── bakage.pl │ │ ├── local_package │ │ │ ├── local.pl │ │ │ └── scryer-manifest.pl │ │ ├── scryer-manifest.pl │ │ ├── main.pl │ │ └── justfile │ ├── manifest_with_git_dependency_already_installed │ │ ├── .gitignore │ │ ├── bakage.pl │ │ ├── scryer-manifest.pl │ │ ├── main.pl │ │ └── justfile │ ├── manifest_with_path_dependency_already_installed │ │ ├── .gitignore │ │ ├── bakage.pl │ │ ├── local_package │ │ │ ├── local.pl │ │ │ └── scryer-manifest.pl │ │ ├── scryer-manifest.pl │ │ ├── main.pl │ │ └── justfile │ ├── invalid_manifest_multiple_licenses_multiple_types │ │ ├── .gitignore │ │ ├── license │ │ ├── bakage.pl │ │ ├── scryer-manifest.pl │ │ ├── main.pl │ │ └── justfile │ ├── manifest_with_git_branch_dependency_already_installed │ │ ├── .gitignore │ │ ├── bakage.pl │ │ ├── scryer-manifest.pl │ │ ├── main.pl │ │ └── justfile │ ├── manifest_with_git_hash_dependency_already_installed │ │ ├── .gitignore │ │ ├── bakage.pl │ │ ├── scryer-manifest.pl │ │ ├── main.pl │ │ └── justfile │ └── manifest_with_git_tag_dependency_already_installed │ │ ├── .gitignore │ │ ├── bakage.pl │ │ ├── scryer-manifest.pl │ │ ├── main.pl │ │ └── justfile ├── justfile └── utils │ ├── report_test.sh │ ├── assert.pl │ └── testing.pl ├── .gitignore ├── shellcheckrc ├── src ├── shebang.sh ├── qupak.pl ├── cli.pl ├── validation.pl └── bakage.pl ├── .github └── workflows │ ├── ci.yml │ └── release.yml ├── flake.nix ├── UNLICENSE ├── flake.lock ├── justfile ├── CONTRIBUTING.md ├── README.md └── scripts └── ensure_dependencies.sh /.envrc: -------------------------------------------------------------------------------- 1 | use flake 2 | -------------------------------------------------------------------------------- /example/.gitignore: -------------------------------------------------------------------------------- 1 | scryer_libs -------------------------------------------------------------------------------- /example/bakage.pl: -------------------------------------------------------------------------------- 1 | ../build/bakage.pl -------------------------------------------------------------------------------- /tests/snapshot/cases/cli_help.status: -------------------------------------------------------------------------------- 1 | 0 -------------------------------------------------------------------------------- /tests/snapshot/cases/cli_help.stderr: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/snapshot/cases/subdirectory_imports.stderr: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/integration/use_module/.gitignore: -------------------------------------------------------------------------------- 1 | scryer_libs -------------------------------------------------------------------------------- /tests/snapshot/.gitignore: -------------------------------------------------------------------------------- 1 | dump/ 2 | tmp_snapshot/ 3 | -------------------------------------------------------------------------------- /tests/snapshot/cases/subdirectory_imports.status: -------------------------------------------------------------------------------- 1 | 0 -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.direnv 2 | *.gen 3 | build 4 | src/scripts.pl 5 | -------------------------------------------------------------------------------- /tests/integration/use_module/bakage.pl: -------------------------------------------------------------------------------- 1 | ../../../build/bakage.pl -------------------------------------------------------------------------------- /tests/integration/manifest_with_git_dependency/.gitignore: -------------------------------------------------------------------------------- 1 | scryer_libs -------------------------------------------------------------------------------- /tests/integration/invalid_manifest_missing_license/.gitignore: -------------------------------------------------------------------------------- 1 | scryer_libs -------------------------------------------------------------------------------- /tests/integration/invalid_manifest_missing_name/.gitignore: -------------------------------------------------------------------------------- 1 | scryer_libs -------------------------------------------------------------------------------- /tests/integration/invalid_manifest_multiple_name/.gitignore: -------------------------------------------------------------------------------- 1 | scryer_libs -------------------------------------------------------------------------------- /tests/integration/manifest_multiple_dependencies/.gitignore: -------------------------------------------------------------------------------- 1 | scryer_libs -------------------------------------------------------------------------------- /tests/integration/manifest_with_git_hash_dependency/.gitignore: -------------------------------------------------------------------------------- 1 | scryer_libs -------------------------------------------------------------------------------- /tests/integration/manifest_with_git_tag_dependency/.gitignore: -------------------------------------------------------------------------------- 1 | scryer_libs -------------------------------------------------------------------------------- /tests/integration/manifest_with_no_dependencies/.gitignore: -------------------------------------------------------------------------------- 1 | scryer_libs -------------------------------------------------------------------------------- /tests/integration/manifest_with_path_dependency/.gitignore: -------------------------------------------------------------------------------- 1 | scryer_libs -------------------------------------------------------------------------------- /tests/integration/invalid_manifest_invalid_license_path/.gitignore: -------------------------------------------------------------------------------- 1 | scryer_libs -------------------------------------------------------------------------------- /tests/integration/invalid_manifest_multiple_licenses/.gitignore: -------------------------------------------------------------------------------- 1 | scryer_libs -------------------------------------------------------------------------------- /tests/integration/invalid_manifest_multiple_main_file/.gitignore: -------------------------------------------------------------------------------- 1 | scryer_libs -------------------------------------------------------------------------------- /tests/integration/manifest_with_git_branch_dependency/.gitignore: -------------------------------------------------------------------------------- 1 | scryer_libs -------------------------------------------------------------------------------- /tests/integration/manifest_with_no_dependency_predicate/.gitignore: -------------------------------------------------------------------------------- 1 | scryer_libs -------------------------------------------------------------------------------- /tests/integration/all_invalid_dependencies_stop_installation/.gitignore: -------------------------------------------------------------------------------- 1 | scryer_libs -------------------------------------------------------------------------------- /tests/integration/invalid_manifest_missing_name/bakage.pl: -------------------------------------------------------------------------------- 1 | ../../../build/bakage.pl -------------------------------------------------------------------------------- /tests/integration/invalid_manifest_multiple_name/bakage.pl: -------------------------------------------------------------------------------- 1 | ../../../build/bakage.pl -------------------------------------------------------------------------------- /tests/integration/manifest_multiple_dependencies/bakage.pl: -------------------------------------------------------------------------------- 1 | ../../../build/bakage.pl -------------------------------------------------------------------------------- /tests/integration/manifest_with_git_dependency/bakage.pl: -------------------------------------------------------------------------------- 1 | ../../../build/bakage.pl -------------------------------------------------------------------------------- /tests/integration/manifest_with_git_hash_dependency/license: -------------------------------------------------------------------------------- 1 | this is a license 2 | -------------------------------------------------------------------------------- /tests/integration/manifest_with_no_dependencies/bakage.pl: -------------------------------------------------------------------------------- 1 | ../../../build/bakage.pl -------------------------------------------------------------------------------- /tests/integration/manifest_with_path_dependency/bakage.pl: -------------------------------------------------------------------------------- 1 | ../../../build/bakage.pl -------------------------------------------------------------------------------- /tests/integration/one_invalid_dependency_stop_installation/.gitignore: -------------------------------------------------------------------------------- 1 | scryer_libs -------------------------------------------------------------------------------- /example/local_package/local.pl: -------------------------------------------------------------------------------- 1 | :- module(dummy, [local/1]). 2 | 3 | local(exist). 4 | -------------------------------------------------------------------------------- /tests/integration/invalid_manifest_missing_license/bakage.pl: -------------------------------------------------------------------------------- 1 | ../../../build/bakage.pl -------------------------------------------------------------------------------- /tests/integration/invalid_manifest_multiple_licenses/bakage.pl: -------------------------------------------------------------------------------- 1 | ../../../build/bakage.pl -------------------------------------------------------------------------------- /tests/integration/invalid_manifest_multiple_main_file/bakage.pl: -------------------------------------------------------------------------------- 1 | ../../../build/bakage.pl -------------------------------------------------------------------------------- /tests/integration/manifest_multiple_dependencies_with_all_errors/.gitignore: -------------------------------------------------------------------------------- 1 | scryer_libs -------------------------------------------------------------------------------- /tests/integration/manifest_multiple_dependencies_with_one_error/.gitignore: -------------------------------------------------------------------------------- 1 | scryer_libs -------------------------------------------------------------------------------- /tests/integration/manifest_with_git_branch_dependency/bakage.pl: -------------------------------------------------------------------------------- 1 | ../../../build/bakage.pl -------------------------------------------------------------------------------- /tests/integration/manifest_with_git_dependency_already_installed/.gitignore: -------------------------------------------------------------------------------- 1 | scryer_libs -------------------------------------------------------------------------------- /tests/integration/manifest_with_git_hash_dependency/bakage.pl: -------------------------------------------------------------------------------- 1 | ../../../build/bakage.pl -------------------------------------------------------------------------------- /tests/integration/manifest_with_git_tag_dependency/bakage.pl: -------------------------------------------------------------------------------- 1 | ../../../build/bakage.pl -------------------------------------------------------------------------------- /tests/integration/manifest_with_path_dependency_already_installed/.gitignore: -------------------------------------------------------------------------------- 1 | scryer_libs -------------------------------------------------------------------------------- /tests/integration/invalid_manifest_invalid_license_path/bakage.pl: -------------------------------------------------------------------------------- 1 | ../../../build/bakage.pl -------------------------------------------------------------------------------- /tests/integration/invalid_manifest_multiple_licenses_multiple_types/.gitignore: -------------------------------------------------------------------------------- 1 | scryer_libs -------------------------------------------------------------------------------- /tests/integration/manifest_with_git_branch_dependency_already_installed/.gitignore: -------------------------------------------------------------------------------- 1 | scryer_libs -------------------------------------------------------------------------------- /tests/integration/manifest_with_git_hash_dependency_already_installed/.gitignore: -------------------------------------------------------------------------------- 1 | scryer_libs -------------------------------------------------------------------------------- /tests/integration/manifest_with_git_tag_dependency_already_installed/.gitignore: -------------------------------------------------------------------------------- 1 | scryer_libs -------------------------------------------------------------------------------- /tests/integration/manifest_with_no_dependency_predicate/bakage.pl: -------------------------------------------------------------------------------- 1 | ../../../build/bakage.pl -------------------------------------------------------------------------------- /tests/integration/one_invalid_dependency_stop_installation/bakage.pl: -------------------------------------------------------------------------------- 1 | ../../../build/bakage.pl -------------------------------------------------------------------------------- /example/local_package/scryer-manifest.pl: -------------------------------------------------------------------------------- 1 | name("local_package"). 2 | main_file("local.pl"). 3 | -------------------------------------------------------------------------------- /tests/integration/all_invalid_dependencies_stop_installation/bakage.pl: -------------------------------------------------------------------------------- 1 | ../../../build/bakage.pl -------------------------------------------------------------------------------- /tests/integration/invalid_manifest_multiple_licenses_multiple_types/license: -------------------------------------------------------------------------------- 1 | this is a license 2 | -------------------------------------------------------------------------------- /tests/integration/manifest_multiple_dependencies_with_all_errors/bakage.pl: -------------------------------------------------------------------------------- 1 | ../../../build/bakage.pl -------------------------------------------------------------------------------- /tests/integration/manifest_multiple_dependencies_with_one_error/bakage.pl: -------------------------------------------------------------------------------- 1 | ../../../build/bakage.pl -------------------------------------------------------------------------------- /tests/integration/manifest_with_git_dependency_already_installed/bakage.pl: -------------------------------------------------------------------------------- 1 | ../../../build/bakage.pl -------------------------------------------------------------------------------- /tests/integration/invalid_manifest_multiple_licenses_multiple_types/bakage.pl: -------------------------------------------------------------------------------- 1 | ../../../build/bakage.pl -------------------------------------------------------------------------------- /tests/integration/manifest_with_git_hash_dependency_already_installed/bakage.pl: -------------------------------------------------------------------------------- 1 | ../../../build/bakage.pl -------------------------------------------------------------------------------- /tests/integration/manifest_with_git_tag_dependency_already_installed/bakage.pl: -------------------------------------------------------------------------------- 1 | ../../../build/bakage.pl -------------------------------------------------------------------------------- /tests/integration/manifest_with_path_dependency_already_installed/bakage.pl: -------------------------------------------------------------------------------- 1 | ../../../build/bakage.pl -------------------------------------------------------------------------------- /tests/integration/manifest_with_git_branch_dependency_already_installed/bakage.pl: -------------------------------------------------------------------------------- 1 | ../../../build/bakage.pl -------------------------------------------------------------------------------- /tests/integration/use_module/local_package/local.pl: -------------------------------------------------------------------------------- 1 | :- module(dummy, [local/1]). 2 | 3 | local(exist). 4 | -------------------------------------------------------------------------------- /tests/integration/use_module/local_package/scryer-manifest.pl: -------------------------------------------------------------------------------- 1 | name("local_package"). 2 | main_file("local.pl"). 3 | -------------------------------------------------------------------------------- /tests/integration/manifest_multiple_dependencies/local_package/local.pl: -------------------------------------------------------------------------------- 1 | :- module(dummy, [local/1]). 2 | 3 | local(exist). 4 | -------------------------------------------------------------------------------- /tests/integration/manifest_with_path_dependency/local_package/local.pl: -------------------------------------------------------------------------------- 1 | :- module(dummy, [local/1]). 2 | 3 | local(exist). 4 | -------------------------------------------------------------------------------- /shellcheckrc: -------------------------------------------------------------------------------- 1 | # We intentionally use /* as a command glob in the "shebang" for bakage.pl 2 | disable=SC1127 3 | disable=SC2211 4 | -------------------------------------------------------------------------------- /tests/integration/manifest_multiple_dependencies/local_package/scryer-manifest.pl: -------------------------------------------------------------------------------- 1 | name("local_package"). 2 | main_file("local.pl"). 3 | -------------------------------------------------------------------------------- /tests/integration/manifest_with_path_dependency/local_package/scryer-manifest.pl: -------------------------------------------------------------------------------- 1 | name("local_package"). 2 | main_file("local.pl"). 3 | -------------------------------------------------------------------------------- /tests/integration/one_invalid_dependency_stop_installation/local_package/local.pl: -------------------------------------------------------------------------------- 1 | :- module(dummy, [local/1]). 2 | 3 | local(exist). 4 | -------------------------------------------------------------------------------- /tests/integration/all_invalid_dependencies_stop_installation/local_package/local.pl: -------------------------------------------------------------------------------- 1 | :- module(dummy, [local/1]). 2 | 3 | local(exist). 4 | -------------------------------------------------------------------------------- /tests/integration/manifest_multiple_dependencies_with_all_errors/local_package/local.pl: -------------------------------------------------------------------------------- 1 | :- module(dummy, [local/1]). 2 | 3 | local(exist). 4 | -------------------------------------------------------------------------------- /tests/integration/manifest_multiple_dependencies_with_one_error/local_package/local.pl: -------------------------------------------------------------------------------- 1 | :- module(dummy, [local/1]). 2 | 3 | local(exist). 4 | -------------------------------------------------------------------------------- /tests/integration/all_invalid_dependencies_stop_installation/local_package/scryer-manifest.pl: -------------------------------------------------------------------------------- 1 | name("local_package"). 2 | main_file("local.pl"). 3 | -------------------------------------------------------------------------------- /tests/integration/manifest_with_path_dependency_already_installed/local_package/local.pl: -------------------------------------------------------------------------------- 1 | :- module(dummy, [local/1]). 2 | 3 | local(exist). 4 | -------------------------------------------------------------------------------- /tests/integration/one_invalid_dependency_stop_installation/local_package/scryer-manifest.pl: -------------------------------------------------------------------------------- 1 | name("local_package"). 2 | main_file("local.pl"). 3 | -------------------------------------------------------------------------------- /tests/integration/manifest_multiple_dependencies_with_all_errors/local_package/scryer-manifest.pl: -------------------------------------------------------------------------------- 1 | name("local_package"). 2 | main_file("local.pl"). 3 | -------------------------------------------------------------------------------- /tests/integration/manifest_multiple_dependencies_with_one_error/local_package/scryer-manifest.pl: -------------------------------------------------------------------------------- 1 | name("local_package"). 2 | main_file("local.pl"). 3 | -------------------------------------------------------------------------------- /tests/integration/manifest_with_path_dependency_already_installed/local_package/scryer-manifest.pl: -------------------------------------------------------------------------------- 1 | name("local_package"). 2 | main_file("local.pl"). 3 | -------------------------------------------------------------------------------- /tests/integration/manifest_with_no_dependency_predicate/scryer-manifest.pl: -------------------------------------------------------------------------------- 1 | name("manifest_with_no_dependency_predicate"). 2 | license(name("UNLICENSE")). 3 | main_file("main.pl"). 4 | -------------------------------------------------------------------------------- /tests/integration/manifest_with_no_dependencies/scryer-manifest.pl: -------------------------------------------------------------------------------- 1 | name("manifest_with_no_dependencies"). 2 | main_file("main.pl"). 3 | license(name("UNLICENSE")). 4 | dependencies([]). 5 | -------------------------------------------------------------------------------- /tests/snapshot/cases/subdirectory_imports.in/my_module.pl: -------------------------------------------------------------------------------- 1 | :- module(my_module, [greet/1]). 2 | 3 | greet(Name) :- 4 | write('Hello, '), 5 | write(Name), 6 | write('!'), nl. 7 | -------------------------------------------------------------------------------- /tests/snapshot/justfile: -------------------------------------------------------------------------------- 1 | test: 2 | ./snapshot.sh test 3 | 4 | dump: 5 | ./snapshot.sh dump 6 | 7 | snapshot: 8 | ./snapshot.sh snapshot 9 | 10 | clean: 11 | rm -rf dump 12 | -------------------------------------------------------------------------------- /tests/snapshot/cases/subdirectory_imports.in/scryer-manifest.pl: -------------------------------------------------------------------------------- 1 | name("bakage-repro"). 2 | license(name("UNLICENSE")). 3 | dependencies([ 4 | dependency("testing", git("https://github.com/bakaq/testing.pl.git")) 5 | ]). 6 | -------------------------------------------------------------------------------- /tests/integration/manifest_with_path_dependency/scryer-manifest.pl: -------------------------------------------------------------------------------- 1 | name("manifest_with_no_dependencies"). 2 | main_file("main.pl"). 3 | license(name("UNLICENSE")). 4 | dependencies([ 5 | dependency("test", path("./local_package")) 6 | ]). 7 | -------------------------------------------------------------------------------- /tests/integration/invalid_manifest_missing_name/scryer-manifest.pl: -------------------------------------------------------------------------------- 1 | main_file("main.pl"). 2 | license(name("UNLICENSE")). 3 | dependencies([ 4 | dependency("test", git("https://github.com/constraintAutomaton/test-prolog-package-manager.git")) 5 | ]). 6 | -------------------------------------------------------------------------------- /tests/snapshot/cases/subdirectory_imports.in/tests/test_my_module.pl: -------------------------------------------------------------------------------- 1 | :- use_module('../../../../../build/bakage.pl'). 2 | :- use_module(pkg(testing)). 3 | :- use_module('../my_module.pl'). 4 | 5 | test("greet succeeds", ( 6 | greet('World') 7 | )). 8 | -------------------------------------------------------------------------------- /tests/integration/invalid_manifest_missing_license/scryer-manifest.pl: -------------------------------------------------------------------------------- 1 | name("name_of_the_package"). 2 | main_file("main.pl"). 3 | dependencies([ 4 | dependency("test", git("https://github.com/constraintAutomaton/test-prolog-package-manager.git")) 5 | ]). 6 | -------------------------------------------------------------------------------- /tests/integration/manifest_with_path_dependency_already_installed/scryer-manifest.pl: -------------------------------------------------------------------------------- 1 | name("manifest_with_no_dependencies"). 2 | main_file("main.pl"). 3 | license(name("UNLICENSE")). 4 | dependencies([ 5 | dependency("test", path("./local_package")) 6 | ]). 7 | -------------------------------------------------------------------------------- /tests/integration/manifest_with_git_dependency/scryer-manifest.pl: -------------------------------------------------------------------------------- 1 | name("manifest_with_no_dependencies"). 2 | main_file("main.pl"). 3 | license(name("UNLICENSE")). 4 | dependencies([ 5 | dependency("test", git("https://github.com/constraintAutomaton/test-prolog-package-manager.git")) 6 | ]). 7 | -------------------------------------------------------------------------------- /tests/integration/manifest_with_git_dependency_already_installed/scryer-manifest.pl: -------------------------------------------------------------------------------- 1 | name("manifest_with_no_dependencies"). 2 | main_file("main.pl"). 3 | license(name("UNLICENSE")). 4 | dependencies([ 5 | dependency("test", git("https://github.com/constraintAutomaton/test-prolog-package-manager.git")) 6 | ]). 7 | -------------------------------------------------------------------------------- /tests/integration/manifest_with_git_tag_dependency/scryer-manifest.pl: -------------------------------------------------------------------------------- 1 | name("manifest_with_no_dependencies"). 2 | main_file("main.pl"). 3 | license(name("UNLICENSE")). 4 | dependencies([ 5 | dependency("test", git("https://github.com/constraintAutomaton/test-prolog-package-manager.git", tag("tag"))) 6 | ]). 7 | -------------------------------------------------------------------------------- /tests/integration/invalid_manifest_invalid_license_path/scryer-manifest.pl: -------------------------------------------------------------------------------- 1 | name("name_of_the_package"). 2 | main_file("main.pl"). 3 | license(name("UNLICENSE"), path("./do_not_exist")). 4 | dependencies([ 5 | dependency("test", git("https://github.com/constraintAutomaton/test-prolog-package-manager.git")) 6 | ]). 7 | -------------------------------------------------------------------------------- /tests/integration/manifest_with_no_dependencies/main.pl: -------------------------------------------------------------------------------- 1 | :- use_module(bakage). 2 | :- use_module('../../utils/testing.pl'). 3 | :- use_module('../../utils/assert.pl'). 4 | 5 | main :- 6 | run_tests. 7 | 8 | test("test if no dependencies are installed", (pkg_install(X), test_eq(X, [validate_manifest-success]))). 9 | -------------------------------------------------------------------------------- /src/shebang.sh: -------------------------------------------------------------------------------- 1 | /*usr/bin/env true 2 | 3 | set -eu 4 | 5 | type scryer-prolog > /dev/null 2> /dev/null \ 6 | && exec scryer-prolog -f -g "bakage:run" "$0" -- "$@" 7 | 8 | echo "No known supported Prolog implementation available in PATH." 9 | echo "Try to install Scryer Prolog." 10 | exit 1 11 | #*/ 12 | 13 | -------------------------------------------------------------------------------- /tests/integration/invalid_manifest_multiple_licenses/scryer-manifest.pl: -------------------------------------------------------------------------------- 1 | name("name_of_the_package"). 2 | main_file("main.pl"). 3 | license(name("UNLICENSE")). 4 | license(name("LICENSE")). 5 | dependencies([ 6 | dependency("test", git("https://github.com/constraintAutomaton/test-prolog-package-manager.git")) 7 | ]). 8 | -------------------------------------------------------------------------------- /tests/integration/invalid_manifest_multiple_main_file/scryer-manifest.pl: -------------------------------------------------------------------------------- 1 | name("name_of_the_package"). 2 | main_file("main.pl"). 3 | main_file("main_alt.pl"). 4 | license(name("UNLICENSE")). 5 | dependencies([ 6 | dependency("test", git("https://github.com/constraintAutomaton/test-prolog-package-manager.git")) 7 | ]). 8 | -------------------------------------------------------------------------------- /tests/integration/invalid_manifest_multiple_name/scryer-manifest.pl: -------------------------------------------------------------------------------- 1 | name("name_of_the_package"). 2 | name("name_of_the_package_2"). 3 | main_file("main.pl"). 4 | license(name("UNLICENSE")). 5 | dependencies([ 6 | dependency("test", git("https://github.com/constraintAutomaton/test-prolog-package-manager.git")) 7 | ]). 8 | -------------------------------------------------------------------------------- /tests/integration/manifest_with_git_branch_dependency/scryer-manifest.pl: -------------------------------------------------------------------------------- 1 | name("manifest_with_no_dependencies"). 2 | main_file("main.pl"). 3 | license(name("UNLICENSE")). 4 | dependencies([ 5 | dependency("test", git("https://github.com/constraintAutomaton/test-prolog-package-manager.git", branch("branch"))) 6 | ]). 7 | -------------------------------------------------------------------------------- /tests/integration/manifest_with_git_tag_dependency_already_installed/scryer-manifest.pl: -------------------------------------------------------------------------------- 1 | name("manifest_with_no_dependencies"). 2 | main_file("main.pl"). 3 | license(name("UNLICENSE")). 4 | dependencies([ 5 | dependency("test", git("https://github.com/constraintAutomaton/test-prolog-package-manager.git", tag("tag"))) 6 | ]). 7 | -------------------------------------------------------------------------------- /tests/integration/manifest_with_no_dependency_predicate/main.pl: -------------------------------------------------------------------------------- 1 | :- use_module(bakage). 2 | :- use_module('../../utils/testing.pl'). 3 | :- use_module('../../utils/assert.pl'). 4 | 5 | main :- 6 | run_tests. 7 | 8 | test("test if no dependencies are installed", (pkg_install(X), test_eq(X, [validate_manifest-success]))). 9 | -------------------------------------------------------------------------------- /tests/integration/manifest_with_git_branch_dependency_already_installed/scryer-manifest.pl: -------------------------------------------------------------------------------- 1 | name("manifest_with_no_dependencies"). 2 | main_file("main.pl"). 3 | license(name("UNLICENSE")). 4 | dependencies([ 5 | dependency("test", git("https://github.com/constraintAutomaton/test-prolog-package-manager.git", branch("branch"))) 6 | ]). 7 | -------------------------------------------------------------------------------- /tests/integration/manifest_with_no_dependencies/justfile: -------------------------------------------------------------------------------- 1 | test: 2 | #!/bin/sh 3 | printf "%s\n" "Testing the installation of a git branch dependency" 4 | set -eu 5 | 6 | rm -rf scryer_libs && rm -f manifest-lock.pl 7 | scryer-prolog main.pl -g "catch(main, _, halt(1)) ." 8 | rm -rf scryer_libs && rm -f manifest-lock.pl 9 | -------------------------------------------------------------------------------- /tests/integration/invalid_manifest_multiple_licenses_multiple_types/scryer-manifest.pl: -------------------------------------------------------------------------------- 1 | name("name_of_the_package"). 2 | main_file("main.pl"). 3 | license(name("UNLICENSE")). 4 | license(name("UNLICENSE"), path("./license")). 5 | dependencies([ 6 | dependency("test", git("https://github.com/constraintAutomaton/test-prolog-package-manager.git")) 7 | ]). 8 | -------------------------------------------------------------------------------- /tests/integration/manifest_with_no_dependency_predicate/justfile: -------------------------------------------------------------------------------- 1 | test: 2 | #!/bin/sh 3 | printf "%s\n" "Testing the installation of a git branch dependency" 4 | set -eu 5 | 6 | rm -rf scryer_libs && rm -f manifest-lock.pl 7 | scryer-prolog main.pl -g "catch(main, _, halt(1)) ." 8 | rm -rf scryer_libs && rm -f manifest-lock.pl 9 | -------------------------------------------------------------------------------- /tests/integration/manifest_with_git_hash_dependency/scryer-manifest.pl: -------------------------------------------------------------------------------- 1 | name("manifest_with_no_dependencies"). 2 | main_file("main.pl"). 3 | license(name("UNLICENSE"), path("./license")). 4 | dependencies([ 5 | dependency("test", git("https://github.com/constraintAutomaton/test-prolog-package-manager.git", hash("d19fefc1d7907f6675e181601bb9b8b94561b441"))) 6 | ]). 7 | -------------------------------------------------------------------------------- /tests/integration/manifest_with_git_hash_dependency_already_installed/scryer-manifest.pl: -------------------------------------------------------------------------------- 1 | name("manifest_with_no_dependencies"). 2 | main_file("main.pl"). 3 | license(name("UNLICENSE")). 4 | dependencies([ 5 | dependency("test", git("https://github.com/constraintAutomaton/test-prolog-package-manager.git", hash("d19fefc1d7907f6675e181601bb9b8b94561b441"))) 6 | ]). 7 | -------------------------------------------------------------------------------- /tests/integration/use_module/justfile: -------------------------------------------------------------------------------- 1 | test: 2 | #!/bin/sh 3 | set -eu 4 | 5 | printf "%s\n" "Testing the usage of the dependencies" 6 | 7 | rm -rf scryer_libs && rm -f manifest-lock.pl 8 | 9 | ./bakage.pl install 10 | 11 | scryer-prolog main.pl -g "catch(main, _, halt(1)) ." 12 | 13 | rm -rf scryer_libs && rm -f manifest-lock.pl 14 | 15 | -------------------------------------------------------------------------------- /example/justfile: -------------------------------------------------------------------------------- 1 | test: clean-install 2 | scryer-prolog -f main.pl -g "catch(main, _, halt(1)),halt" 3 | 4 | clean: 5 | rm --recursive --force scryer_libs 6 | 7 | install: 8 | ./bakage.pl install 9 | 10 | # This is so that we don't depend on the ordering of dependencies, 11 | # which doesn't seem to be documented anywhere. 12 | [doc] 13 | clean-install: clean 14 | just install 15 | -------------------------------------------------------------------------------- /tests/integration/one_invalid_dependency_stop_installation/justfile: -------------------------------------------------------------------------------- 1 | test: 2 | #!/bin/sh 3 | set -eu 4 | 5 | . ../../utils/report_test.sh 6 | 7 | printf "%s\n" "Testing of one invalid dependency" 8 | 9 | rm -rf scryer_libs && rm -f manifest-lock.pl 10 | 11 | scryer-prolog main.pl -g "catch(main, _, halt(1)) ." 12 | 13 | rm -rf scryer_libs && rm -f manifest-lock.pl 14 | -------------------------------------------------------------------------------- /tests/integration/all_invalid_dependencies_stop_installation/justfile: -------------------------------------------------------------------------------- 1 | test: 2 | #!/bin/sh 3 | set -eu 4 | 5 | . ../../utils/report_test.sh 6 | 7 | printf "%s\n" "Testing of all invalid dependencies" 8 | 9 | rm -rf scryer_libs && rm -f manifest-lock.pl 10 | 11 | scryer-prolog main.pl -g "catch(main, _, halt(1)) ." 12 | 13 | rm -rf scryer_libs && rm -f manifest-lock.pl 14 | -------------------------------------------------------------------------------- /tests/integration/manifest_multiple_dependencies_with_all_errors/justfile: -------------------------------------------------------------------------------- 1 | test: 2 | #!/bin/sh 3 | set -eu 4 | 5 | . ../../utils/report_test.sh 6 | 7 | printf "%s\n" "Testing installation of multiple dependencies where all the dependencies have errors" 8 | 9 | rm -rf scryer_libs && rm -f manifest-lock.pl 10 | 11 | scryer-prolog main.pl -g "catch(main, _, halt(1)) ." 12 | 13 | rm -rf scryer_libs && rm -f manifest-lock.pl 14 | -------------------------------------------------------------------------------- /tests/snapshot/cases/subdirectory_imports.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | bakage() { 4 | ../../../../build/bakage.pl "$@" 5 | } 6 | 7 | echo "=== Installing dependencies ===" 8 | bakage install 9 | 10 | echo "" 11 | echo "=== Running test from project root ===" 12 | scryer-prolog tests/test_my_module.pl -g "run_tests, halt." 13 | 14 | echo "" 15 | echo "=== Running test from subdirectory ===" 16 | cd tests 17 | scryer-prolog test_my_module.pl -g "run_tests, halt." 18 | -------------------------------------------------------------------------------- /tests/snapshot/cases/subdirectory_imports.stdout: -------------------------------------------------------------------------------- 1 | === Installing dependencies === 2 | Ensuring is installed: git("https://github.com/bakaq/testing.pl.git") 3 | 4 | === Running test from project root === 5 | Running tests in module user. 6 | test "greet succeeds" ... Hello, World! 7 | succeeded 8 | 9 | === Running test from subdirectory === 10 | Running tests in module user. 11 | test "greet succeeds" ... Hello, World! 12 | succeeded 13 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | push: 4 | branches: 5 | - main 6 | pull_request: 7 | workflow_dispatch: 8 | 9 | jobs: 10 | 11 | run-ci: 12 | name: Run CI 13 | runs-on: ubuntu-latest 14 | steps: 15 | - name: git checkout 16 | uses: actions/checkout@v4 17 | - name: Install Nix 18 | uses: cachix/install-nix-action@v31 19 | - name: Run CI 20 | run: | 21 | nix develop --ignore-env --command \ 22 | just ci 23 | -------------------------------------------------------------------------------- /tests/justfile: -------------------------------------------------------------------------------- 1 | test: snapshot-tests integration-tests 2 | 3 | integration-tests: 4 | #!/bin/sh 5 | set -eu 6 | 7 | exit_code=0 8 | 9 | for dir in integration/*; do 10 | if (cd "$dir" && just test); then 11 | printf "%s\n" "Test in $dir passed" 12 | else 13 | printf "%s\n" "Test in $dir FAILED" 14 | exit_code=1 15 | fi 16 | done 17 | 18 | exit "$exit_code" 19 | 20 | snapshot-tests: 21 | just snapshot/test 22 | 23 | clean: 24 | just snapshot/clean 25 | -------------------------------------------------------------------------------- /tests/integration/manifest_with_path_dependency/main.pl: -------------------------------------------------------------------------------- 1 | :- use_module(bakage). 2 | :- use_module('../../utils/testing.pl'). 3 | :- use_module('../../utils/assert.pl'). 4 | :- use_module(library(pio)). 5 | :- use_module(library(format)). 6 | 7 | main :- 8 | run_tests. 9 | 10 | test("the package report is valid", ( 11 | pkg_install(X), 12 | Expected = [ 13 | validate_manifest-success, 14 | install_dependency(dependency("test", path("./local_package")))-success 15 | ], 16 | test_eq(X, Expected) 17 | ) 18 | ). 19 | -------------------------------------------------------------------------------- /tests/utils/report_test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | print_test_result() { 4 | test_name="$1" 5 | result="$2" 6 | 7 | case "$result" in 8 | success) 9 | color_code="\033[1;32m" # Bold green 10 | status_label="succeeded" 11 | ;; 12 | failure) 13 | color_code="\033[1;31m" # Bold red 14 | status_label="failed" 15 | ;; 16 | *) 17 | exit 1 18 | esac 19 | 20 | printf '\ttest "%s" ... %b%s%b\n' "$test_name" "$color_code" "$status_label" "\033[0m" 21 | } 22 | -------------------------------------------------------------------------------- /tests/integration/manifest_with_path_dependency_already_installed/main.pl: -------------------------------------------------------------------------------- 1 | :- use_module(bakage). 2 | :- use_module('../../utils/testing.pl'). 3 | :- use_module('../../utils/assert.pl'). 4 | :- use_module(library(pio)). 5 | :- use_module(library(format)). 6 | 7 | main :- 8 | pkg_install(_), 9 | run_tests. 10 | 11 | test("the package report is valid", ( 12 | pkg_install(X), 13 | Expected = [ 14 | validate_manifest-success, 15 | do_nothing(dependency("test", path("./local_package")))-success 16 | ], 17 | test_eq(X, Expected) 18 | ) 19 | ). 20 | -------------------------------------------------------------------------------- /tests/integration/manifest_with_git_dependency/main.pl: -------------------------------------------------------------------------------- 1 | :- use_module(bakage). 2 | :- use_module('../../utils/testing.pl'). 3 | :- use_module('../../utils/assert.pl'). 4 | :- use_module(library(pio)). 5 | :- use_module(library(format)). 6 | 7 | main :- 8 | run_tests. 9 | 10 | test("the package report is valid", ( 11 | pkg_install(X), 12 | Expected = [ 13 | validate_manifest-success, 14 | install_dependency(dependency("test", git("https://github.com/constraintAutomaton/test-prolog-package-manager.git")))-success 15 | ], 16 | test_eq(X, Expected) 17 | ) 18 | ). 19 | -------------------------------------------------------------------------------- /tests/integration/manifest_with_git_tag_dependency/main.pl: -------------------------------------------------------------------------------- 1 | :- use_module(bakage). 2 | :- use_module('../../utils/testing.pl'). 3 | :- use_module('../../utils/assert.pl'). 4 | :- use_module(library(pio)). 5 | :- use_module(library(format)). 6 | 7 | main :- 8 | run_tests. 9 | 10 | test("the package report is valid", ( 11 | pkg_install(X), 12 | Expected = [ 13 | validate_manifest-success, 14 | install_dependency(dependency("test", git("https://github.com/constraintAutomaton/test-prolog-package-manager.git", tag("tag"))))-success 15 | ], 16 | test_eq(X, Expected) 17 | ) 18 | ). 19 | -------------------------------------------------------------------------------- /tests/integration/manifest_with_git_branch_dependency/main.pl: -------------------------------------------------------------------------------- 1 | :- use_module(bakage). 2 | :- use_module('../../utils/testing.pl'). 3 | :- use_module('../../utils/assert.pl'). 4 | :- use_module(library(pio)). 5 | :- use_module(library(format)). 6 | 7 | main :- 8 | run_tests. 9 | 10 | test("the package report is valid", ( 11 | pkg_install(X), 12 | Expected = [ 13 | validate_manifest-success, 14 | install_dependency(dependency("test", git("https://github.com/constraintAutomaton/test-prolog-package-manager.git", branch("branch"))))-success 15 | ], 16 | test_eq(X, Expected) 17 | ) 18 | ). 19 | -------------------------------------------------------------------------------- /tests/integration/invalid_manifest_multiple_name/main.pl: -------------------------------------------------------------------------------- 1 | :- use_module(bakage). 2 | :- use_module('../../utils/testing.pl'). 3 | :- use_module('../../utils/assert.pl'). 4 | :- use_module(library(pio)). 5 | :- use_module(library(format)). 6 | 7 | main :- 8 | run_tests. 9 | 10 | test("the package report is valid", ( 11 | pkg_install(X), 12 | Expected = [ 13 | validate_manifest_name-error("the package has multiple 'name'"), 14 | validate_manifest_main_file-success, 15 | validate_manifest_license-success, 16 | validate_dependencies-success 17 | ], 18 | test_eq(X, Expected) 19 | ) 20 | ). 21 | -------------------------------------------------------------------------------- /tests/integration/manifest_with_git_dependency_already_installed/main.pl: -------------------------------------------------------------------------------- 1 | :- use_module(bakage). 2 | :- use_module('../../utils/testing.pl'). 3 | :- use_module('../../utils/assert.pl'). 4 | :- use_module(library(pio)). 5 | :- use_module(library(format)). 6 | 7 | main :- 8 | pkg_install(_), 9 | run_tests. 10 | 11 | test("the package report is valid", ( 12 | pkg_install(X), 13 | Expected = [ 14 | validate_manifest-success, 15 | do_nothing(dependency("test", git("https://github.com/constraintAutomaton/test-prolog-package-manager.git")))-success 16 | ], 17 | test_eq(X, Expected) 18 | ) 19 | ). 20 | -------------------------------------------------------------------------------- /tests/integration/invalid_manifest_multiple_licenses/main.pl: -------------------------------------------------------------------------------- 1 | :- use_module(bakage). 2 | :- use_module('../../utils/testing.pl'). 3 | :- use_module('../../utils/assert.pl'). 4 | :- use_module(library(pio)). 5 | :- use_module(library(format)). 6 | 7 | main :- 8 | run_tests. 9 | 10 | test("the package report is valid", ( 11 | pkg_install(X), 12 | Expected = [ 13 | validate_manifest_name-success, 14 | validate_manifest_main_file-success, 15 | validate_manifest_license-error("the package has multiple 'license'"), 16 | validate_dependencies-success 17 | ], 18 | test_eq(X, Expected) 19 | ) 20 | ). 21 | -------------------------------------------------------------------------------- /tests/integration/invalid_manifest_multiple_main_file/main.pl: -------------------------------------------------------------------------------- 1 | :- use_module(bakage). 2 | :- use_module('../../utils/testing.pl'). 3 | :- use_module('../../utils/assert.pl'). 4 | :- use_module(library(pio)). 5 | :- use_module(library(format)). 6 | 7 | main :- 8 | run_tests. 9 | 10 | test("the package report is valid", ( 11 | pkg_install(X), 12 | Expected = [ 13 | validate_manifest_name-success, 14 | validate_manifest_main_file-error("the package has multiple 'main_file'"), 15 | validate_manifest_license-success, 16 | validate_dependencies-success 17 | ], 18 | test_eq(X, Expected) 19 | ) 20 | ). 21 | -------------------------------------------------------------------------------- /tests/integration/invalid_manifest_invalid_license_path/main.pl: -------------------------------------------------------------------------------- 1 | :- use_module(bakage). 2 | :- use_module('../../utils/testing.pl'). 3 | :- use_module('../../utils/assert.pl'). 4 | :- use_module(library(pio)). 5 | :- use_module(library(format)). 6 | 7 | main :- 8 | run_tests. 9 | 10 | test("the package report is valid", ( 11 | pkg_install(X), 12 | Expected = [ 13 | validate_manifest_name-success, 14 | validate_manifest_main_file-success, 15 | validate_manifest_license-error("the path of the license is not valid"), 16 | validate_dependencies-success 17 | ], 18 | test_eq(X, Expected) 19 | ) 20 | ). 21 | -------------------------------------------------------------------------------- /tests/integration/manifest_with_git_tag_dependency_already_installed/main.pl: -------------------------------------------------------------------------------- 1 | :- use_module(bakage). 2 | :- use_module('../../utils/testing.pl'). 3 | :- use_module('../../utils/assert.pl'). 4 | :- use_module(library(pio)). 5 | :- use_module(library(format)). 6 | 7 | main :- 8 | pkg_install(_), 9 | run_tests. 10 | 11 | test("the package report is valid", ( 12 | pkg_install(X), 13 | Expected = [ 14 | validate_manifest-success, 15 | do_nothing(dependency("test", git("https://github.com/constraintAutomaton/test-prolog-package-manager.git", tag("tag"))))-success 16 | ], 17 | test_eq(X, Expected) 18 | ) 19 | ). 20 | -------------------------------------------------------------------------------- /tests/integration/invalid_manifest_multiple_licenses_multiple_types/main.pl: -------------------------------------------------------------------------------- 1 | :- use_module(bakage). 2 | :- use_module('../../utils/testing.pl'). 3 | :- use_module('../../utils/assert.pl'). 4 | :- use_module(library(pio)). 5 | :- use_module(library(format)). 6 | 7 | main :- 8 | run_tests. 9 | 10 | test("the package report is valid", ( 11 | pkg_install(X), 12 | Expected = [ 13 | validate_manifest_name-success, 14 | validate_manifest_main_file-success, 15 | validate_manifest_license-error("the package has multiple 'license'"), 16 | validate_dependencies-success 17 | ], 18 | test_eq(X, Expected) 19 | ) 20 | ). 21 | -------------------------------------------------------------------------------- /tests/integration/manifest_with_git_hash_dependency/main.pl: -------------------------------------------------------------------------------- 1 | :- use_module(bakage). 2 | :- use_module('../../utils/testing.pl'). 3 | :- use_module('../../utils/assert.pl'). 4 | :- use_module(library(pio)). 5 | :- use_module(library(format)). 6 | 7 | main :- 8 | run_tests. 9 | 10 | test("the package report is valid", ( 11 | pkg_install(X), 12 | Expected = [ 13 | validate_manifest-success, 14 | install_dependency(dependency("test", git("https://github.com/constraintAutomaton/test-prolog-package-manager.git", hash("d19fefc1d7907f6675e181601bb9b8b94561b441"))))-success 15 | ], 16 | test_eq(X, Expected) 17 | ) 18 | ). 19 | -------------------------------------------------------------------------------- /tests/integration/use_module/main.pl: -------------------------------------------------------------------------------- 1 | :- use_module(bakage). 2 | :- use_module(pkg(test)). 3 | :- use_module(pkg(test_branch)). 4 | :- use_module(pkg(test_tag)). 5 | :- use_module(pkg(test_hash)). 6 | :- use_module(pkg(test_local)). 7 | :- use_module('../../utils/testing.pl'). 8 | :- use_module('../../utils/assert.pl'). 9 | 10 | main :- 11 | run_tests. 12 | 13 | test("test if the git dependency work", (main_code(exist))). 14 | test("test if the branch dependency works", (tag(exist))). 15 | test("test if the tag dependency works", (branch(exist))). 16 | test("test if the hash dependency works", (hash(exist))). 17 | test("test if the local dependency works", (local(exist))). 18 | -------------------------------------------------------------------------------- /tests/integration/manifest_with_git_branch_dependency_already_installed/main.pl: -------------------------------------------------------------------------------- 1 | :- use_module(bakage). 2 | :- use_module('../../utils/testing.pl'). 3 | :- use_module('../../utils/assert.pl'). 4 | :- use_module(library(pio)). 5 | :- use_module(library(format)). 6 | 7 | main :- 8 | pkg_install(_), 9 | run_tests. 10 | 11 | test("the package report is valid", ( 12 | pkg_install(X), 13 | Expected = [ 14 | validate_manifest-success, 15 | do_nothing(dependency("test", git("https://github.com/constraintAutomaton/test-prolog-package-manager.git", branch("branch"))))-success 16 | ], 17 | test_eq(X, Expected) 18 | ) 19 | ). 20 | -------------------------------------------------------------------------------- /tests/integration/manifest_with_git_hash_dependency_already_installed/main.pl: -------------------------------------------------------------------------------- 1 | :- use_module(bakage). 2 | :- use_module('../../utils/testing.pl'). 3 | :- use_module('../../utils/assert.pl'). 4 | :- use_module(library(pio)). 5 | :- use_module(library(format)). 6 | 7 | main :- 8 | pkg_install(_), 9 | run_tests. 10 | 11 | test("the package report is valid", ( 12 | pkg_install(X), 13 | Expected = [ 14 | validate_manifest-success, 15 | do_nothing(dependency("test", git("https://github.com/constraintAutomaton/test-prolog-package-manager.git", hash("d19fefc1d7907f6675e181601bb9b8b94561b441"))))-success 16 | ], 17 | test_eq(X, Expected) 18 | ) 19 | ). 20 | -------------------------------------------------------------------------------- /tests/integration/invalid_manifest_missing_name/main.pl: -------------------------------------------------------------------------------- 1 | :- use_module(bakage). 2 | :- use_module('../../utils/testing.pl'). 3 | :- use_module('../../utils/assert.pl'). 4 | :- use_module(library(pio)). 5 | :- use_module(library(format)). 6 | 7 | main :- 8 | run_tests. 9 | 10 | test("the package report is valid", ( 11 | pkg_install(X), 12 | Expected = [ 13 | validate_manifest_name-error("the \'name\' of the package is not defined or does not have the a predicate of the form \'name(N)\'"), 14 | validate_manifest_main_file-success, 15 | validate_manifest_license-success, 16 | validate_dependencies-success 17 | ], 18 | test_eq(X, Expected) 19 | ) 20 | ). 21 | -------------------------------------------------------------------------------- /tests/integration/manifest_multiple_dependencies_with_all_errors/scryer-manifest.pl: -------------------------------------------------------------------------------- 1 | name("name_of_the_package"). 2 | main_file("main.pl"). 3 | license(name("UNLICENSE")). 4 | dependencies([ 5 | dependency("test", git("https://github.com/constraintAutomaton/test-prolog-package-manager_.git")), 6 | dependency("test_branch", git("https://github.com/constraintAutomaton/test-prolog-package-manager.git", branch("branch_"))), 7 | dependency("test_tag", git("https://github.com/constraintAutomaton/test-prolog-package-manager.git", tag("tag_"))), 8 | dependency("test_hash", git("https://github.com/constraintAutomaton/test-prolog-package-manager.git", hash("abc"))), 9 | dependency("test_local", path("./local_package_")) 10 | ]). 11 | -------------------------------------------------------------------------------- /tests/integration/use_module/scryer-manifest.pl: -------------------------------------------------------------------------------- 1 | name("name_of_the_package"). 2 | main_file("main.pl"). 3 | license(name("UNLICENSE")). 4 | 5 | dependencies([ 6 | dependency("test", git("https://github.com/constraintAutomaton/test-prolog-package-manager.git")), 7 | dependency("test_branch", git("https://github.com/constraintAutomaton/test-prolog-package-manager.git", branch("branch"))), 8 | dependency("test_tag", git("https://github.com/constraintAutomaton/test-prolog-package-manager.git", tag("tag"))), 9 | dependency("test_hash", git("https://github.com/constraintAutomaton/test-prolog-package-manager.git", hash("d19fefc1d7907f6675e181601bb9b8b94561b441"))), 10 | dependency("test_local", path("./local_package")) 11 | ]). 12 | -------------------------------------------------------------------------------- /tests/integration/invalid_manifest_missing_name/justfile: -------------------------------------------------------------------------------- 1 | test: 2 | #!/bin/sh 3 | 4 | set -eu 5 | . ../../utils/report_test.sh 6 | 7 | printf "%s\n" "Testing invalid manifest with a missing name" 8 | 9 | rm -rf scryer_libs && rm -f manifest-lock.pl 10 | scryer-prolog main.pl -g "catch(main, _, halt(1)) ." 11 | 12 | exitCode=0 13 | 14 | if [ ! -d "./scryer_libs/packages/test" ]; then 15 | print_test_result "the package folder does not exist in the dependencies" success 16 | else 17 | exitCode=1 18 | print_test_result "the package folder does not exist in the dependencies" failure 19 | fi 20 | 21 | 22 | rm -rf scryer_libs && rm -f manifest-lock.pl 23 | 24 | exit ${exitCode} 25 | -------------------------------------------------------------------------------- /tests/integration/invalid_manifest_multiple_name/justfile: -------------------------------------------------------------------------------- 1 | test: 2 | #!/bin/sh 3 | 4 | set -eu 5 | . ../../utils/report_test.sh 6 | 7 | printf "%s\n" "Testing invalid manifest with multiple names" 8 | 9 | rm -rf scryer_libs && rm -f manifest-lock.pl 10 | scryer-prolog main.pl -g "catch(main, _, halt(1)) ." 11 | 12 | exitCode=0 13 | 14 | if [ ! -d "./scryer_libs/packages/test" ]; then 15 | print_test_result "the package folder does not exist in the dependencies" success 16 | else 17 | exitCode=1 18 | print_test_result "the package folder does not exist in the dependencies" failure 19 | fi 20 | 21 | 22 | rm -rf scryer_libs && rm -f manifest-lock.pl 23 | 24 | exit ${exitCode} 25 | -------------------------------------------------------------------------------- /tests/integration/manifest_multiple_dependencies/scryer-manifest.pl: -------------------------------------------------------------------------------- 1 | name("name_of_the_package"). 2 | main_file("main.pl"). 3 | license(name("UNLICENSE")). 4 | dependencies([ 5 | dependency("test", git("https://github.com/constraintAutomaton/test-prolog-package-manager.git")), 6 | dependency("test_branch", git("https://github.com/constraintAutomaton/test-prolog-package-manager.git", branch("branch"))), 7 | dependency("test_tag", git("https://github.com/constraintAutomaton/test-prolog-package-manager.git", tag("tag"))), 8 | dependency("test_hash", git("https://github.com/constraintAutomaton/test-prolog-package-manager.git", hash("d19fefc1d7907f6675e181601bb9b8b94561b441"))), 9 | dependency("test_local", path("./local_package")) 10 | ]). 11 | -------------------------------------------------------------------------------- /tests/integration/invalid_manifest_missing_license/justfile: -------------------------------------------------------------------------------- 1 | test: 2 | #!/bin/sh 3 | 4 | set -eu 5 | . ../../utils/report_test.sh 6 | 7 | printf "%s\n" "Testing invalid manifest with a missing license" 8 | 9 | rm -rf scryer_libs && rm -f manifest-lock.pl 10 | scryer-prolog main.pl -g "catch(main, _, halt(1)) ." 11 | 12 | exitCode=0 13 | 14 | if [ ! -d "./scryer_libs/packages/test" ]; then 15 | print_test_result "the package folder does not exist in the dependencies" success 16 | else 17 | exitCode=1 18 | print_test_result "the package folder does not exist in the dependencies" failure 19 | fi 20 | 21 | 22 | rm -rf scryer_libs && rm -f manifest-lock.pl 23 | 24 | exit ${exitCode} 25 | -------------------------------------------------------------------------------- /tests/integration/invalid_manifest_multiple_licenses/justfile: -------------------------------------------------------------------------------- 1 | test: 2 | #!/bin/sh 3 | 4 | set -eu 5 | . ../../utils/report_test.sh 6 | 7 | printf "%s\n" "Testing invalid manifest with multiple licenses" 8 | 9 | rm -rf scryer_libs && rm -f manifest-lock.pl 10 | scryer-prolog main.pl -g "catch(main, _, halt(1)) ." 11 | 12 | exitCode=0 13 | 14 | if [ ! -d "./scryer_libs/packages/test" ]; then 15 | print_test_result "the package folder does not exist in the dependencies" success 16 | else 17 | exitCode=1 18 | print_test_result "the package folder does not exist in the dependencies" failure 19 | fi 20 | 21 | 22 | rm -rf scryer_libs && rm -f manifest-lock.pl 23 | 24 | exit ${exitCode} 25 | -------------------------------------------------------------------------------- /tests/integration/invalid_manifest_invalid_license_path/justfile: -------------------------------------------------------------------------------- 1 | test: 2 | #!/bin/sh 3 | 4 | set -eu 5 | . ../../utils/report_test.sh 6 | 7 | printf "%s\n" "Testing invalid manifest with invalid license path" 8 | 9 | rm -rf scryer_libs && rm -f manifest-lock.pl 10 | scryer-prolog main.pl -g "catch(main, _, halt(1)) ." 11 | 12 | exitCode=0 13 | 14 | if [ ! -d "./scryer_libs/packages/test" ]; then 15 | print_test_result "the package folder does not exist in the dependencies" success 16 | else 17 | exitCode=1 18 | print_test_result "the package folder does not exist in the dependencies" failure 19 | fi 20 | 21 | 22 | rm -rf scryer_libs && rm -f manifest-lock.pl 23 | 24 | exit ${exitCode} 25 | -------------------------------------------------------------------------------- /tests/integration/invalid_manifest_missing_license/main.pl: -------------------------------------------------------------------------------- 1 | :- use_module(bakage). 2 | :- use_module('../../utils/testing.pl'). 3 | :- use_module('../../utils/assert.pl'). 4 | :- use_module(library(pio)). 5 | :- use_module(library(format)). 6 | 7 | main :- 8 | run_tests. 9 | 10 | test("the package report is valid", ( 11 | pkg_install(X), 12 | Expected = [ 13 | validate_manifest_name-success, 14 | validate_manifest_main_file-success, 15 | validate_manifest_license-error("the \'license\' of the package is not defined or does not have the a predicate of the form \'license(name(N));license(name(N), path(P))\'"), 16 | validate_dependencies-success 17 | ], 18 | test_eq(X, Expected) 19 | ) 20 | ). 21 | -------------------------------------------------------------------------------- /tests/integration/invalid_manifest_multiple_main_file/justfile: -------------------------------------------------------------------------------- 1 | test: 2 | #!/bin/sh 3 | 4 | set -eu 5 | . ../../utils/report_test.sh 6 | 7 | printf "%s\n" "Testing invalid manifest with multiple main files" 8 | 9 | rm -rf scryer_libs && rm -f manifest-lock.pl 10 | scryer-prolog main.pl -g "catch(main, _, halt(1)) ." 11 | 12 | exitCode=0 13 | 14 | if [ ! -d "./scryer_libs/packages/test" ]; then 15 | print_test_result "the package folder does not exist in the dependencies" success 16 | else 17 | exitCode=1 18 | print_test_result "the package folder does not exist in the dependencies" failure 19 | fi 20 | 21 | 22 | rm -rf scryer_libs && rm -f manifest-lock.pl 23 | 24 | exit ${exitCode} 25 | -------------------------------------------------------------------------------- /tests/integration/one_invalid_dependency_stop_installation/scryer-manifest.pl: -------------------------------------------------------------------------------- 1 | name("name_of_the_package"). 2 | main_file("main.pl"). 3 | license(name("UNLICENSE")). 4 | dependencies([ 5 | dependency("test", git("https://github.com/constraintAutomaton/test-prolog-package-manager.git|abc")), 6 | dependency("test_branch", git("https://github.com/constraintAutomaton/test-prolog-package-manager.git", branch("branch"))), 7 | dependency("test_tag", git("https://github.com/constraintAutomaton/test-prolog-package-manager.git", tag("tag"))), 8 | dependency("test_hash", git("https://github.com/constraintAutomaton/test-prolog-package-manager.git", hash("d19fefc1d7907f6675e181601bb9b8b94561b441"))), 9 | dependency("test_local", path("./local_package")) 10 | ]). 11 | -------------------------------------------------------------------------------- /tests/integration/all_invalid_dependencies_stop_installation/scryer-manifest.pl: -------------------------------------------------------------------------------- 1 | name("name_of_the_package"). 2 | main_file("main.pl"). 3 | license(name("UNLICENSE")). 4 | dependencies([ 5 | dependency("test", git("https://github.com/constraintAutomaton/test-prolog-package-manager.git|abc")), 6 | dependency("test_branch", git("https://github.com/constraintAutomaton/test-prolog-package-manager.git", branch("branch;"))), 7 | dependency("test_tag", git("https://github.com/constraintAutomaton/test-prolog-package-manager.git|", tag("tag|"))), 8 | dependency("test_hash", git("https://github.com/constraintAutomaton/test-prolog-package-manager.git;", hash("d19fefc1d7907f6675e181601bb9b8b94561b441|def"))), 9 | dependency("test_local", path("./local_packa|ge")) 10 | ]). 11 | -------------------------------------------------------------------------------- /tests/integration/invalid_manifest_multiple_licenses_multiple_types/justfile: -------------------------------------------------------------------------------- 1 | test: 2 | #!/bin/sh 3 | 4 | set -eu 5 | . ../../utils/report_test.sh 6 | 7 | printf "%s\n" "Testing invalid manifest with multiple licenses of multiple types" 8 | 9 | rm -rf scryer_libs && rm -f manifest-lock.pl 10 | scryer-prolog main.pl -g "catch(main, _, halt(1)) ." 11 | 12 | exitCode=0 13 | 14 | if [ ! -d "./scryer_libs/packages/test" ]; then 15 | print_test_result "the package folder does not exist in the dependencies" success 16 | else 17 | exitCode=1 18 | print_test_result "the package folder does not exist in the dependencies" failure 19 | fi 20 | 21 | 22 | rm -rf scryer_libs && rm -f manifest-lock.pl 23 | 24 | exit ${exitCode} 25 | -------------------------------------------------------------------------------- /tests/integration/manifest_multiple_dependencies_with_one_error/scryer-manifest.pl: -------------------------------------------------------------------------------- 1 | name("name_of_the_package"). 2 | main_file("main.pl"). 3 | license(name("UNLICENSE")). 4 | dependencies([ 5 | dependency("test", git("https://github.com/constraintAutomaton/test-prolog-package-manager.git")), 6 | dependency("test_branch", git("https://github.com/constraintAutomaton/test-prolog-package-manager.git", branch("branch"))), 7 | dependency("test_tag", git("https://github.com/constraintAutomaton/test-prolog-package-manager.git", tag("tag"))), 8 | dependency("test_hash", git("https://github.com/constraintAutomaton/test-prolog-package-manager.git", hash("d19fefc1d7907f6675e181601bb9b8b94561b441"))), 9 | dependency("test_local", path("./local_package")), 10 | dependency("error", path("./bar")) 11 | ]). 12 | -------------------------------------------------------------------------------- /tests/snapshot/cases/cli_help.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | bakage() { 4 | ../../build/bakage.pl "$@" 5 | } 6 | 7 | echo "=== Testing help with various different color configurations ===" 8 | bakage --help --color always 9 | echo "================================================================" 10 | bakage --help --color never 11 | echo "================================================================" 12 | CLICOLOR_FORCE=1 bakage --help 13 | echo "================================================================" 14 | NO_COLOR=1 bakage --help 15 | 16 | echo "=== Install help ===" 17 | bakage install --help --color always 18 | echo "================================================================" 19 | bakage install --help --color never 20 | echo "================================================================" 21 | -------------------------------------------------------------------------------- /tests/utils/assert.pl: -------------------------------------------------------------------------------- 1 | % a Value unified with the Expected value and report to the current output if it does not unified 2 | test_unify(Value, Expected) :- 3 | (nonvar(Value), Value = Expected) -> 4 | true 5 | ; ( 6 | current_output(Out), 7 | phrase_to_stream(("expect value to unified with: \n\n", portray_clause_(Expected), "\nbut got: \n\n", portray_clause_(Value)), Out), 8 | false 9 | ). 10 | 11 | % a Value unified with the Expected value and report to the current output if it does not unified 12 | test_eq(Value, Expected):- 13 | Value == Expected -> 14 | true 15 | ; ( 16 | current_output(Out), 17 | phrase_to_stream(("expected: \n\n", portray_clause_(Expected), "\nbut got: \n\n", portray_clause_(Value)), Out), 18 | false 19 | ). -------------------------------------------------------------------------------- /example/main.pl: -------------------------------------------------------------------------------- 1 | % Loads the package loader 2 | :- use_module(bakage). 3 | 4 | % Loads a package. The argument should be an atom equal to the name of the 5 | % dependency package specified in the `name/1` field of its manifest. 6 | :- use_module(pkg(testing)). 7 | :- use_module(pkg(test_branch)). 8 | :- use_module(pkg(test_tag)). 9 | :- use_module(pkg(test_hash)). 10 | :- use_module(pkg(test_local)). 11 | 12 | % You can then use the predicates exported by the main file of the dependency 13 | % in the rest of the program. 14 | 15 | main :- 16 | % `run_tests/0` is exported by `pkg(testing)` 17 | run_tests, 18 | halt. 19 | 20 | test("test if the branch dependency works", (tag(exist))). 21 | test("test if the tag dependency works", (branch(exist))). 22 | test("test if the hash dependency works", (hash(exist))). 23 | test("test if the local dependency works", (local(exist))). 24 | -------------------------------------------------------------------------------- /flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | description = "An experimental package manager for Scryer Prolog"; 3 | 4 | inputs = { 5 | nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; 6 | flake-utils.url = "github:numtide/flake-utils"; 7 | }; 8 | 9 | outputs = 10 | { 11 | nixpkgs, 12 | flake-utils, 13 | ... 14 | }: 15 | flake-utils.lib.eachDefaultSystem ( 16 | system: 17 | let 18 | pkgs = nixpkgs.legacyPackages.${system}; 19 | in 20 | { 21 | devShells = { 22 | default = pkgs.mkShell { 23 | buildInputs = builtins.attrValues { 24 | inherit (pkgs) 25 | git 26 | just 27 | util-linux 28 | shellcheck 29 | scryer-prolog 30 | ; 31 | }; 32 | }; 33 | }; 34 | } 35 | ); 36 | } 37 | -------------------------------------------------------------------------------- /example/scryer-manifest.pl: -------------------------------------------------------------------------------- 1 | name("name_of_the_package"). 2 | % Optional. The file that will be imported when this package is used. 3 | main_file("main.pl"). 4 | %License 5 | license(name("UNLICENSE")). 6 | % Optional 7 | dependencies([ 8 | % A git url to clone 9 | dependency("testing", git("https://github.com/bakaq/testing.pl.git")), 10 | % A git url to clone at a specific branch 11 | dependency("test_branch", git("https://github.com/constraintAutomaton/test-prolog-package-manager.git", branch("branch"))), 12 | % A git url to clone at a tag 13 | dependency("test_tag", git("https://github.com/constraintAutomaton/test-prolog-package-manager.git", tag("tag"))), 14 | % A git url to clone at a specific commit hash 15 | dependency("test_hash", git("https://github.com/constraintAutomaton/test-prolog-package-manager.git", hash("d19fefc1d7907f6675e181601bb9b8b94561b441"))), 16 | % A path to a local package 17 | dependency("test_local", path("./local_package")) 18 | ]). 19 | -------------------------------------------------------------------------------- /tests/integration/manifest_with_path_dependency/justfile: -------------------------------------------------------------------------------- 1 | test: 2 | #!/bin/sh 3 | set -eu 4 | 5 | . ../../utils/report_test.sh 6 | 7 | printf "%s\n" "Testing the installation of a path dependency" 8 | 9 | rm -rf scryer_libs && rm -f manifest-lock.pl 10 | scryer-prolog main.pl -g "catch(main, _, halt(1)) ." 11 | 12 | exitCode=0 13 | 14 | if [ ! -d "./scryer_libs/packages/test" ]; then 15 | exitCode=1 16 | print_test_result "the package folder exist in the dependencies" failure 17 | else 18 | print_test_result "the package folder exist in the dependencies" success 19 | fi 20 | 21 | if [ ! -f "./scryer_libs/packages/test/scryer-manifest.pl" ]; then 22 | exitCode=1 23 | print_test_result "the manifest of the dependency exist" failure 24 | else 25 | print_test_result "the manifest of the dependency exist" success 26 | fi 27 | 28 | rm -rf scryer_libs && rm -f manifest-lock.pl 29 | 30 | exit ${exitCode} -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | workflow_dispatch: 8 | 9 | permissions: 10 | contents: write 11 | 12 | jobs: 13 | main-release: 14 | if: github.ref_type == 'branch' && github.ref_name == 'main' 15 | runs-on: ubuntu-latest 16 | steps: 17 | - name: Checkout 18 | uses: actions/checkout@v5 19 | - name: Install Nix 20 | uses: cachix/install-nix-action@v31 21 | - name: Build Bakage 22 | run: | 23 | nix develop --ignore-env --command \ 24 | just build 25 | - name: Update tag 26 | run: | 27 | git push origin :refs/tags/main-release 28 | git tag -f main-release 29 | git push origin --tags 30 | - name: Release 31 | uses: softprops/action-gh-release@v2 32 | with: 33 | tag_name: main-release 34 | draft: false 35 | prerelease: true 36 | files: ./build/bakage.pl 37 | -------------------------------------------------------------------------------- /tests/integration/manifest_with_path_dependency_already_installed/justfile: -------------------------------------------------------------------------------- 1 | test: 2 | #!/bin/sh 3 | set -eu 4 | 5 | . ../../utils/report_test.sh 6 | 7 | printf "%s\n" "Testing the installation of a path dependency already installed" 8 | 9 | rm -rf scryer_libs && rm -f manifest-lock.pl 10 | scryer-prolog main.pl -g "catch(main, _, halt(1)) ." 11 | 12 | exitCode=0 13 | 14 | if [ ! -d "./scryer_libs/packages/test" ]; then 15 | exitCode=1 16 | print_test_result "the package folder exist in the dependencies" failure 17 | else 18 | print_test_result "the package folder exist in the dependencies" success 19 | fi 20 | 21 | if [ ! -f "./scryer_libs/packages/test/scryer-manifest.pl" ]; then 22 | exitCode=1 23 | print_test_result "the manifest of the dependency exist" failure 24 | else 25 | print_test_result "the manifest of the dependency exist" success 26 | fi 27 | 28 | rm -rf scryer_libs && rm -f manifest-lock.pl 29 | 30 | exit ${exitCode} -------------------------------------------------------------------------------- /UNLICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /tests/integration/manifest_multiple_dependencies_with_all_errors/main.pl: -------------------------------------------------------------------------------- 1 | :- use_module(bakage). 2 | :- use_module('../../utils/testing.pl'). 3 | :- use_module('../../utils/assert.pl'). 4 | :- use_module(library(ordsets)). 5 | :- use_module(library(pio)). 6 | :- use_module(library(format)). 7 | 8 | main :- 9 | run_tests. 10 | 11 | test("the package report is valid", ( 12 | pkg_install(X), 13 | list_to_ord_set(X, X_Set), 14 | list_to_ord_set([ 15 | validate_manifest-success, 16 | 17 | install_dependency(dependency("test", git("https://github.com/constraintAutomaton/test-prolog-package-manager_.git")))-error(_), 18 | install_dependency(dependency("test_branch", git("https://github.com/constraintAutomaton/test-prolog-package-manager.git", branch("branch_"))))-error(_), 19 | install_dependency(dependency("test_tag", git("https://github.com/constraintAutomaton/test-prolog-package-manager.git", tag("tag_"))))-error(_), 20 | install_dependency(dependency("test_hash", git("https://github.com/constraintAutomaton/test-prolog-package-manager.git", hash("abc"))))-error(_), 21 | install_dependency(dependency("test_local", path("./local_package_")))-error(_) 22 | ], Expected), 23 | test_unify(X_Set, Expected) 24 | ) 25 | ). 26 | -------------------------------------------------------------------------------- /tests/integration/one_invalid_dependency_stop_installation/main.pl: -------------------------------------------------------------------------------- 1 | :- use_module(bakage). 2 | :- use_module('../../utils/testing.pl'). 3 | :- use_module('../../utils/assert.pl'). 4 | :- use_module('../../utils/assert.pl'). 5 | :- use_module(library(ordsets)). 6 | :- use_module(library(pio)). 7 | :- use_module(library(format)). 8 | 9 | main :- 10 | run_tests. 11 | 12 | test("the package report is valid", ( 13 | pkg_install(X), 14 | list_to_ord_set([ 15 | validate_dependency(dependency("test", git("https://github.com/constraintAutomaton/test-prolog-package-manager.git|abc")))-error(_), 16 | validate_dependency(dependency("test_branch", git("https://github.com/constraintAutomaton/test-prolog-package-manager.git", branch("branch"))))-success, 17 | validate_dependency(dependency("test_tag", git("https://github.com/constraintAutomaton/test-prolog-package-manager.git", tag("tag"))))-success, 18 | validate_dependency(dependency("test_hash", git("https://github.com/constraintAutomaton/test-prolog-package-manager.git", hash("d19fefc1d7907f6675e181601bb9b8b94561b441"))))-success, 19 | validate_dependency(dependency("test_local", path("./local_package")))-success 20 | ], Expected), 21 | list_to_ord_set(X, X_Set), 22 | test_unify(X_Set, Expected) 23 | ) 24 | ). 25 | -------------------------------------------------------------------------------- /tests/integration/all_invalid_dependencies_stop_installation/main.pl: -------------------------------------------------------------------------------- 1 | :- use_module(bakage). 2 | :- use_module('../../utils/testing.pl'). 3 | :- use_module('../../utils/assert.pl'). 4 | :- use_module('../../utils/assert.pl'). 5 | :- use_module(library(ordsets)). 6 | :- use_module(library(pio)). 7 | :- use_module(library(format)). 8 | 9 | main :- 10 | run_tests. 11 | 12 | test("the package report is valid", ( 13 | pkg_install(X), 14 | list_to_ord_set([ 15 | validate_dependency(dependency("test", git("https://github.com/constraintAutomaton/test-prolog-package-manager.git|abc")))-error(_), 16 | validate_dependency(dependency("test_branch", git("https://github.com/constraintAutomaton/test-prolog-package-manager.git", branch("branch;"))))-error(_), 17 | validate_dependency(dependency("test_tag", git("https://github.com/constraintAutomaton/test-prolog-package-manager.git|", tag("tag|"))))-error(_), 18 | validate_dependency(dependency("test_hash", git("https://github.com/constraintAutomaton/test-prolog-package-manager.git;", hash("d19fefc1d7907f6675e181601bb9b8b94561b441|def"))))-error(_), 19 | validate_dependency(dependency("test_local", path("./local_packa|ge")))-error(_) 20 | ], Expected), 21 | list_to_ord_set(X, X_Set), 22 | test_unify(X_Set, Expected) 23 | ) 24 | ). -------------------------------------------------------------------------------- /tests/integration/manifest_with_git_dependency/justfile: -------------------------------------------------------------------------------- 1 | test: 2 | #!/bin/sh 3 | 4 | set -eu 5 | . ../../utils/report_test.sh 6 | 7 | printf "%s\n" "Testing the installation of a git dependency" 8 | 9 | rm -rf scryer_libs && rm -f manifest-lock.pl 10 | scryer-prolog main.pl -g "catch(main, _, halt(1)) ." 11 | 12 | exitCode=0 13 | 14 | if [ ! -d "./scryer_libs/packages/test" ]; then 15 | exitCode=1 16 | print_test_result "the package folder exist in the dependencies" failure 17 | else 18 | print_test_result "the package folder exist in the dependencies" success 19 | fi 20 | 21 | if [ ! -f "./scryer_libs/packages/test/scryer-manifest.pl" ]; then 22 | exitCode=1 23 | print_test_result "the manifest of the dependency exist" failure 24 | else 25 | print_test_result "the manifest of the dependency exist" success 26 | fi 27 | 28 | dependencyBranch=$(cd ./scryer_libs/packages/test && git rev-parse --abbrev-ref HEAD) 29 | 30 | if [ "$dependencyBranch" != "main" ]; then 31 | exitCode=1 32 | print_test_result "the dependency is at the correct git branch" failure 33 | else 34 | print_test_result "the dependency is at the correct git branch" success 35 | fi 36 | 37 | rm -rf scryer_libs && rm -f manifest-lock.pl 38 | 39 | exit ${exitCode} -------------------------------------------------------------------------------- /tests/integration/manifest_multiple_dependencies/main.pl: -------------------------------------------------------------------------------- 1 | :- use_module(bakage). 2 | :- use_module('../../utils/testing.pl'). 3 | :- use_module('../../utils/assert.pl'). 4 | :- use_module('../../utils/assert.pl'). 5 | :- use_module(library(ordsets)). 6 | :- use_module(library(pio)). 7 | :- use_module(library(format)). 8 | 9 | main :- 10 | run_tests. 11 | 12 | test("the package report is valid", ( 13 | pkg_install(X), 14 | list_to_ord_set([ 15 | validate_manifest-success, 16 | 17 | install_dependency(dependency("test", git("https://github.com/constraintAutomaton/test-prolog-package-manager.git")))-success, 18 | install_dependency(dependency("test_branch", git("https://github.com/constraintAutomaton/test-prolog-package-manager.git", branch("branch"))))-success, 19 | install_dependency(dependency("test_tag", git("https://github.com/constraintAutomaton/test-prolog-package-manager.git", tag("tag"))))-success, 20 | install_dependency(dependency("test_hash", git("https://github.com/constraintAutomaton/test-prolog-package-manager.git", hash("d19fefc1d7907f6675e181601bb9b8b94561b441"))))-success, 21 | install_dependency(dependency("test_local", path("./local_package")))-success 22 | ], Expected), 23 | list_to_ord_set(X, X_Set), 24 | test_eq(X_Set, Expected) 25 | ) 26 | ). 27 | -------------------------------------------------------------------------------- /tests/integration/manifest_with_git_tag_dependency/justfile: -------------------------------------------------------------------------------- 1 | test: 2 | #!/bin/sh 3 | set -eu 4 | 5 | . ../../utils/report_test.sh 6 | 7 | printf "%s\n" "Testing the installation of a git branch dependency" 8 | 9 | rm -rf scryer_libs && rm -f manifest-lock.pl 10 | scryer-prolog main.pl -g "catch(main, _, halt(1)) ." 11 | 12 | exitCode=0 13 | 14 | if [ ! -d "./scryer_libs/packages/test" ]; then 15 | exitCode=1 16 | print_test_result "the package folder exist in the dependencies" failure 17 | else 18 | print_test_result "the package folder exist in the dependencies" success 19 | fi 20 | 21 | if [ ! -f "./scryer_libs/packages/test/scryer-manifest.pl" ]; then 22 | exitCode=1 23 | print_test_result "the manifest of the dependency exist" failure 24 | else 25 | print_test_result "the manifest of the dependency exist" success 26 | fi 27 | 28 | dependencyTag=$(cd ./scryer_libs/packages/test && git describe --exact-match --tags 2>/dev/null) 29 | 30 | if [ "$dependencyTag" != "tag" ]; then 31 | exitCode=1 32 | print_test_result "the dependency is at the correct git tag" failure 33 | else 34 | print_test_result "the dependency is at the correct git tag" success 35 | fi 36 | 37 | rm -rf scryer_libs && rm -f manifest-lock.pl 38 | 39 | exit ${exitCode} -------------------------------------------------------------------------------- /tests/integration/manifest_with_git_branch_dependency/justfile: -------------------------------------------------------------------------------- 1 | test: 2 | #!/bin/sh 3 | set -eu 4 | 5 | . ../../utils/report_test.sh 6 | 7 | printf "%s\n" "Testing the installation of a git branch dependency" 8 | 9 | rm -rf scryer_libs && rm -f manifest-lock.pl 10 | scryer-prolog main.pl -g "catch(main, _, halt(1)) ." 11 | 12 | exitCode=0 13 | 14 | if [ ! -d "./scryer_libs/packages/test" ]; then 15 | exitCode=1 16 | print_test_result "the package folder exist in the dependencies" failure 17 | else 18 | print_test_result "the package folder exist in the dependencies" success 19 | fi 20 | 21 | if [ ! -f "./scryer_libs/packages/test/scryer-manifest.pl" ]; then 22 | exitCode=1 23 | print_test_result "the manifest of the dependency exist" failure 24 | else 25 | print_test_result "the manifest of the dependency exist" success 26 | fi 27 | 28 | dependencyBranch=$(cd ./scryer_libs/packages/test && git rev-parse --abbrev-ref HEAD) 29 | 30 | if [ "$dependencyBranch" != "branch" ]; then 31 | exitCode=1 32 | print_test_result "the dependency is at the correct git branch" failure 33 | else 34 | print_test_result "the dependency is at the correct git branch" success 35 | fi 36 | 37 | rm -rf scryer_libs && rm -f manifest-lock.pl 38 | 39 | exit ${exitCode} -------------------------------------------------------------------------------- /tests/integration/manifest_with_git_dependency_already_installed/justfile: -------------------------------------------------------------------------------- 1 | test: 2 | #!/bin/sh 3 | 4 | set -eu 5 | . ../../utils/report_test.sh 6 | 7 | printf "%s\n" "Testing the installation of a git dependency already installed" 8 | 9 | rm -rf scryer_libs && rm -f manifest-lock.pl 10 | scryer-prolog main.pl -g "catch(main, _, halt(1)) ." 11 | 12 | exitCode=0 13 | 14 | if [ ! -d "./scryer_libs/packages/test" ]; then 15 | exitCode=1 16 | print_test_result "the package folder exist in the dependencies" failure 17 | else 18 | print_test_result "the package folder exist in the dependencies" success 19 | fi 20 | 21 | if [ ! -f "./scryer_libs/packages/test/scryer-manifest.pl" ]; then 22 | exitCode=1 23 | print_test_result "the manifest of the dependency exist" failure 24 | else 25 | print_test_result "the manifest of the dependency exist" success 26 | fi 27 | 28 | dependencyBranch=$(cd ./scryer_libs/packages/test && git rev-parse --abbrev-ref HEAD) 29 | 30 | if [ "$dependencyBranch" != "main" ]; then 31 | exitCode=1 32 | print_test_result "the dependency is at the correct git branch" failure 33 | else 34 | print_test_result "the dependency is at the correct git branch" success 35 | fi 36 | 37 | rm -rf scryer_libs && rm -f manifest-lock.pl 38 | 39 | exit ${exitCode} -------------------------------------------------------------------------------- /tests/integration/manifest_with_git_hash_dependency/justfile: -------------------------------------------------------------------------------- 1 | test: 2 | #!/bin/sh 3 | set -eu 4 | 5 | . ../../utils/report_test.sh 6 | 7 | printf "%s\n" "Testing the installation of a git hash dependency" 8 | 9 | rm -rf scryer_libs && rm -f manifest-lock.pl 10 | scryer-prolog main.pl -g "catch(main, _, halt(1)) ." 11 | 12 | exitCode=0 13 | 14 | if [ ! -d "./scryer_libs/packages/test" ]; then 15 | exitCode=1 16 | print_test_result "the package folder exist in the dependencies" failure 17 | else 18 | print_test_result "the package folder exist in the dependencies" success 19 | fi 20 | 21 | if [ ! -f "./scryer_libs/packages/test/scryer-manifest.pl" ]; then 22 | exitCode=1 23 | print_test_result "the manifest of the dependency exist" failure 24 | else 25 | print_test_result "the manifest of the dependency exist" success 26 | fi 27 | 28 | dependencyGitHash=$(cd ./scryer_libs/packages/test && git rev-parse HEAD) 29 | 30 | if [ "$dependencyGitHash" != "d19fefc1d7907f6675e181601bb9b8b94561b441" ]; then 31 | exitCode=1 32 | print_test_result "the dependency is at the correct git commit hash" failure 33 | else 34 | print_test_result "the dependency is at the correct git commit hash" success 35 | fi 36 | 37 | rm -rf scryer_libs && rm -f manifest-lock.pl 38 | 39 | exit ${exitCode} -------------------------------------------------------------------------------- /tests/integration/manifest_with_git_tag_dependency_already_installed/justfile: -------------------------------------------------------------------------------- 1 | test: 2 | #!/bin/sh 3 | set -eu 4 | 5 | . ../../utils/report_test.sh 6 | 7 | printf "%s\n" "Testing the installation of a git branch dependency already installed" 8 | 9 | rm -rf scryer_libs && rm -f manifest-lock.pl 10 | scryer-prolog main.pl -g "catch(main, _, halt(1)) ." 11 | 12 | exitCode=0 13 | 14 | if [ ! -d "./scryer_libs/packages/test" ]; then 15 | exitCode=1 16 | print_test_result "the package folder exist in the dependencies" failure 17 | else 18 | print_test_result "the package folder exist in the dependencies" success 19 | fi 20 | 21 | if [ ! -f "./scryer_libs/packages/test/scryer-manifest.pl" ]; then 22 | exitCode=1 23 | print_test_result "the manifest of the dependency exist" failure 24 | else 25 | print_test_result "the manifest of the dependency exist" success 26 | fi 27 | 28 | dependencyTag=$(cd ./scryer_libs/packages/test && git describe --exact-match --tags 2>/dev/null) 29 | 30 | if [ "$dependencyTag" != "tag" ]; then 31 | exitCode=1 32 | print_test_result "the dependency is at the correct git tag" failure 33 | else 34 | print_test_result "the dependency is at the correct git tag" success 35 | fi 36 | 37 | rm -rf scryer_libs && rm -f manifest-lock.pl 38 | 39 | exit ${exitCode} -------------------------------------------------------------------------------- /tests/integration/manifest_with_git_branch_dependency_already_installed/justfile: -------------------------------------------------------------------------------- 1 | test: 2 | #!/bin/sh 3 | set -eu 4 | 5 | . ../../utils/report_test.sh 6 | 7 | printf "%s\n" "Testing the installation of a git branch dependency already installed" 8 | 9 | rm -rf scryer_libs && rm -f manifest-lock.pl 10 | scryer-prolog main.pl -g "catch(main, _, halt(1)) ." 11 | 12 | exitCode=0 13 | 14 | if [ ! -d "./scryer_libs/packages/test" ]; then 15 | exitCode=1 16 | print_test_result "the package folder exist in the dependencies" failure 17 | else 18 | print_test_result "the package folder exist in the dependencies" success 19 | fi 20 | 21 | if [ ! -f "./scryer_libs/packages/test/scryer-manifest.pl" ]; then 22 | exitCode=1 23 | print_test_result "the manifest of the dependency exist" failure 24 | else 25 | print_test_result "the manifest of the dependency exist" success 26 | fi 27 | 28 | dependencyBranch=$(cd ./scryer_libs/packages/test && git rev-parse --abbrev-ref HEAD) 29 | 30 | if [ "$dependencyBranch" != "branch" ]; then 31 | exitCode=1 32 | print_test_result "the dependency is at the correct git branch" failure 33 | else 34 | print_test_result "the dependency is at the correct git branch" success 35 | fi 36 | 37 | rm -rf scryer_libs && rm -f manifest-lock.pl 38 | 39 | exit ${exitCode} -------------------------------------------------------------------------------- /tests/integration/manifest_multiple_dependencies_with_one_error/main.pl: -------------------------------------------------------------------------------- 1 | :- use_module(bakage). 2 | :- use_module('../../utils/testing.pl'). 3 | :- use_module('../../utils/assert.pl'). 4 | :- use_module(library(ordsets)). 5 | :- use_module(library(pio)). 6 | :- use_module(library(format)). 7 | 8 | main :- 9 | run_tests. 10 | 11 | test("the package report is valid", ( 12 | pkg_install(X), 13 | list_to_ord_set(X, X_Set), 14 | list_to_ord_set([ 15 | validate_manifest-success, 16 | 17 | install_dependency(dependency("test", git("https://github.com/constraintAutomaton/test-prolog-package-manager.git")))-success, 18 | install_dependency(dependency("test_branch", git("https://github.com/constraintAutomaton/test-prolog-package-manager.git", branch("branch"))))-success, 19 | install_dependency(dependency("test_tag", git("https://github.com/constraintAutomaton/test-prolog-package-manager.git", tag("tag"))))-success, 20 | install_dependency(dependency("test_hash", git("https://github.com/constraintAutomaton/test-prolog-package-manager.git", hash("d19fefc1d7907f6675e181601bb9b8b94561b441"))))-success, 21 | install_dependency(dependency("test_local", path("./local_package")))-success, 22 | install_dependency(dependency("error", path("./bar")))-error(_) 23 | ], Expected), 24 | test_unify(X_Set, Expected) 25 | ) 26 | ). 27 | -------------------------------------------------------------------------------- /tests/integration/manifest_with_git_hash_dependency_already_installed/justfile: -------------------------------------------------------------------------------- 1 | test: 2 | #!/bin/sh 3 | set -eu 4 | 5 | . ../../utils/report_test.sh 6 | 7 | printf "%s\n" "Testing the installation of a git hash dependency already installed" 8 | 9 | rm -rf scryer_libs && rm -f manifest-lock.pl 10 | scryer-prolog main.pl -g "catch(main, _, halt(1)) ." 11 | 12 | exitCode=0 13 | 14 | if [ ! -d "./scryer_libs/packages/test" ]; then 15 | exitCode=1 16 | print_test_result "the package folder exist in the dependencies" failure 17 | else 18 | print_test_result "the package folder exist in the dependencies" success 19 | fi 20 | 21 | if [ ! -f "./scryer_libs/packages/test/scryer-manifest.pl" ]; then 22 | exitCode=1 23 | print_test_result "the manifest of the dependency exist" failure 24 | else 25 | print_test_result "the manifest of the dependency exist" success 26 | fi 27 | 28 | dependencyGitHash=$(cd ./scryer_libs/packages/test && git rev-parse HEAD) 29 | 30 | if [ "$dependencyGitHash" != "d19fefc1d7907f6675e181601bb9b8b94561b441" ]; then 31 | exitCode=1 32 | print_test_result "the dependency is at the correct git commit hash" failure 33 | else 34 | print_test_result "the dependency is at the correct git commit hash" success 35 | fi 36 | 37 | rm -rf scryer_libs && rm -f manifest-lock.pl 38 | 39 | exit ${exitCode} -------------------------------------------------------------------------------- /flake.lock: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": { 3 | "flake-utils": { 4 | "inputs": { 5 | "systems": "systems" 6 | }, 7 | "locked": { 8 | "lastModified": 1731533236, 9 | "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", 10 | "owner": "numtide", 11 | "repo": "flake-utils", 12 | "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", 13 | "type": "github" 14 | }, 15 | "original": { 16 | "owner": "numtide", 17 | "repo": "flake-utils", 18 | "type": "github" 19 | } 20 | }, 21 | "nixpkgs": { 22 | "locked": { 23 | "lastModified": 1759632233, 24 | "narHash": "sha256-krgZxGAIIIKFJS+UB0l8do3sYUDWJc75M72tepmVMzE=", 25 | "owner": "NixOS", 26 | "repo": "nixpkgs", 27 | "rev": "d7f52a7a640bc54c7bb414cca603835bf8dd4b10", 28 | "type": "github" 29 | }, 30 | "original": { 31 | "owner": "NixOS", 32 | "ref": "nixpkgs-unstable", 33 | "repo": "nixpkgs", 34 | "type": "github" 35 | } 36 | }, 37 | "root": { 38 | "inputs": { 39 | "flake-utils": "flake-utils", 40 | "nixpkgs": "nixpkgs" 41 | } 42 | }, 43 | "systems": { 44 | "locked": { 45 | "lastModified": 1681028828, 46 | "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", 47 | "owner": "nix-systems", 48 | "repo": "default", 49 | "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", 50 | "type": "github" 51 | }, 52 | "original": { 53 | "owner": "nix-systems", 54 | "repo": "default", 55 | "type": "github" 56 | } 57 | } 58 | }, 59 | "root": "root", 60 | "version": 7 61 | } 62 | -------------------------------------------------------------------------------- /justfile: -------------------------------------------------------------------------------- 1 | BUILD_NAME := "bakage.pl" 2 | 3 | # Auto build bakage on changes 4 | watch-dev: 5 | watchexec -w scripts -w src just build 6 | 7 | # Shows all the tasks 8 | default: 9 | @just --list 10 | 11 | [private] 12 | ensure-build-directory: 13 | mkdir -p build 14 | 15 | # Builds the bakage.pl file 16 | build: codegen-scripts 17 | cat ./src/shebang.sh > "./build/{{BUILD_NAME}}" 18 | cat ./src/bakage.pl >> "./build/{{BUILD_NAME}}" 19 | printf "\n" >> "./build/{{BUILD_NAME}}" 20 | cat ./src/qupak.pl >> "./build/{{BUILD_NAME}}" 21 | printf "\n" >> "./build/{{BUILD_NAME}}" 22 | cat ./src/validation.pl >> "./build/{{BUILD_NAME}}" 23 | printf "\n" >> "./build/{{BUILD_NAME}}" 24 | cat ./src/cli.pl >> "./build/{{BUILD_NAME}}" 25 | printf "\n" >> "./build/{{BUILD_NAME}}" 26 | cat ./build/scripts.pl >> "./build/{{BUILD_NAME}}" 27 | chmod +x "./build/{{BUILD_NAME}}" 28 | 29 | [private] 30 | codegen-scripts: ensure-build-directory 31 | #!/bin/sh 32 | set -eu 33 | 34 | touch ./build/scripts.pl 35 | 36 | for file in scripts/*.sh; do 37 | script_string=$(scryer-prolog -f -g " 38 | use_module(library(pio)), 39 | use_module(library(dcgs)), 40 | phrase_from_file(seq(Script),\"${file}\"), 41 | write_term(Script, [quoted(true),double_quotes(true)]), 42 | halt. 43 | ") 44 | script_name=$(basename -s .sh "${file}") 45 | printf '%s\n' "script_string(\"${script_name}\", ${script_string})." >> ./build/scripts.pl 46 | done 47 | 48 | # Run all lints 49 | lint: lint-sh 50 | 51 | [private] 52 | lint-sh: 53 | shellcheck -s sh -S warning ./**/*.sh 54 | 55 | # Runs all the checks made in CI 56 | ci: 57 | just lint-sh 58 | just test 59 | 60 | # Runs all the tests 61 | test: build 62 | just example/test 63 | just tests/test 64 | 65 | # Cleans everything that is generated during builds or tests 66 | clean: 67 | just example/clean 68 | just tests/clean 69 | rm -rf build 70 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## Development environment 2 | 3 | ### Nix 4 | 5 | This repository has a nix flake that has everything you will need to run the tests. 6 | You can just use `nix develop` or [`direnv`](https://direnv.net/) with the provided `.envrc`. 7 | 8 | ### Non-Nix 9 | 10 | You will need [Scryer Prolog](https://github.com/mthom/scryer-prolog) `0.10`, 11 | [just](https://github.com/casey/just) and git. 12 | 13 | ## Running the tests 14 | 15 | You can run `just test` in the root directory and it will run all the tests, same as CI. You can 16 | also run `just ci` to run the entire CI. To run _exactly_ what the CI runs, do 17 | `nix develop --ignore-env --command just ci`. That will use the exact dependencies that 18 | are used in CI and defined in the `flake.nix`. 19 | 20 | ## Writing tests 21 | 22 | ### Snapshot tests 23 | 24 | The snapshot tests are in the `tests/snapshot/` directory. The idea is that we run an arbitrary 25 | script and check that everything it does what we expect. 26 | 27 | A test case is composed of the following files in the `tests/snapshot/cases/` directory: 28 | 29 | - The test script `test_name.sh` that runs the test. 30 | - (Optional) A `test_name.stdin` file whose contents will be passed to the test script in stdin. 31 | - (Optional) A `test_name.in` directory where the script will be run. Can be used to provide files 32 | for the test to operate on. 33 | - (Can be generated) A `test_name.stdout` with the expected stdout of the test. 34 | - (Can be generated) A `test_name.stderr` with the expected stderr of the test. 35 | - (Can be generated) A `test_name.status` with the expected exit status code of the test. 36 | 37 | The last 3 files can be generated from the current behavior of the test with `just snapshot`. 38 | 39 | In the `tests/snapshot/` directory, you can use the following just commands to deal with snapshot 40 | tests: 41 | 42 | - `just test` actually runs and checks the tests. 43 | - `just dump` runs the tests and puts the generated files in a `dump/` directory for inspection 44 | without checking them. Useful for manual inspection. 45 | - `just snapshot` runs the tests and puts the generated files in the `cases/` directory. 46 | Useful if you are pretty sure the current behavior is correct. 47 | 48 | -------------------------------------------------------------------------------- /tests/snapshot/cases/cli_help.stdout: -------------------------------------------------------------------------------- 1 | === Testing help with various different color configurations === 2 | Bakage: an experimental package manager for Prolog 3 | 4 | Usage: bakage [OPTIONS] [COMMAND] 5 | 6 | Options: 7 | --color When to use color. Valid options: auto, always, never 8 | -h, --help Print help 9 | 10 | Commands: 11 | install Installs the dependencies of the current package 12 | ================================================================ 13 | Bakage: an experimental package manager for Prolog 14 | 15 | Usage: bakage [OPTIONS] [COMMAND] 16 | 17 | Options: 18 | --color When to use color. Valid options: auto, always, never 19 | -h, --help Print help 20 | 21 | Commands: 22 | install Installs the dependencies of the current package 23 | ================================================================ 24 | Bakage: an experimental package manager for Prolog 25 | 26 | Usage: bakage [OPTIONS] [COMMAND] 27 | 28 | Options: 29 | --color When to use color. Valid options: auto, always, never 30 | -h, --help Print help 31 | 32 | Commands: 33 | install Installs the dependencies of the current package 34 | ================================================================ 35 | Bakage: an experimental package manager for Prolog 36 | 37 | Usage: bakage [OPTIONS] [COMMAND] 38 | 39 | Options: 40 | --color When to use color. Valid options: auto, always, never 41 | -h, --help Print help 42 | 43 | Commands: 44 | install Installs the dependencies of the current package 45 | === Install help === 46 | Install package dependencies 47 | 48 | Usage: bakage install [OPTIONS] 49 | 50 | Options: 51 | --color When to use color. Valid options: auto, always, never 52 | -h, --help Print help 53 | ================================================================ 54 | Install package dependencies 55 | 56 | Usage: bakage install [OPTIONS] 57 | 58 | Options: 59 | --color When to use color. Valid options: auto, always, never 60 | -h, --help Print help 61 | ================================================================ 62 | -------------------------------------------------------------------------------- /tests/snapshot/snapshot.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -eu 4 | 5 | snapshot_test() { 6 | test_file=$(realpath "$1") 7 | test_name=$(basename "$test_file" ".sh") 8 | 9 | printf 'Snapshoting test "%s"\n' "$test_name" 10 | 11 | if [ -f "cases/${test_name}.stdin" ]; then 12 | stdin_file=$(realpath "cases/${test_name}.stdin") 13 | else 14 | stdin_file="/dev/null" 15 | fi 16 | 17 | stdout_file=$(realpath "tmp_snapshot/${test_name}.stdout") 18 | stderr_file=$(realpath "tmp_snapshot/${test_name}.stderr") 19 | status_file=$(realpath "tmp_snapshot/${test_name}.status") 20 | 21 | original_pwd=$(pwd) 22 | has_in_dir=false 23 | if [ -d "cases/${test_name}.in" ]; then 24 | has_in_dir=true 25 | cp -r "cases/${test_name}.in" "cases/${test_name}.in.bak" 26 | cd "cases/${test_name}.in" 27 | fi 28 | 29 | status_code=0 30 | sh "$test_file" \ 31 | < "$stdin_file" \ 32 | > "$stdout_file" \ 33 | 2> "$stderr_file" \ 34 | || status_code="$?" 35 | printf '%s' "$status_code" > "$status_file" 36 | 37 | if [ "$has_in_dir" = true ]; then 38 | cd "$original_pwd" 39 | rm -rf "cases/${test_name}.in" 40 | mv "cases/${test_name}.in.bak" "cases/${test_name}.in" 41 | fi 42 | } 43 | 44 | make_snapshot() { 45 | mkdir -p tmp_snapshot 46 | for test_file in cases/*.sh; do 47 | snapshot_test "$test_file" 48 | done 49 | } 50 | 51 | diff_test() { 52 | test_name="$1" 53 | 54 | printf 'Diffing test "%s"\n' "$test_name" 55 | 56 | status_code=0 57 | for suffix in stdout stderr status; do 58 | diff --unified "cases/${test_name}.${suffix}" "tmp_snapshot/${test_name}.${suffix}" \ 59 | || status_code=1 60 | done 61 | return "$status_code" 62 | } 63 | 64 | case "$1" in 65 | dump) 66 | make_snapshot 67 | rm -rf dump 68 | mv tmp_snapshot dump 69 | ;; 70 | snapshot) 71 | make_snapshot 72 | mv tmp_snapshot/* . 73 | rm -rf tmp_snapshot 74 | ;; 75 | test) 76 | make_snapshot 77 | overall_status_code=0 78 | for test_file in cases/*.sh; do 79 | test_name=$(basename "$test_file" ".sh") 80 | diff_test "$test_name" || overall_status_code=1 81 | done 82 | rm -rf tmp_snapshot 83 | exit "$overall_status_code" 84 | ;; 85 | *) 86 | printf '%s\n' "Unknown command" 87 | exit 1 88 | ;; 89 | esac 90 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Bakage: An experimental package manager for Scryer Prolog 2 | 3 | This project is intended to be a testing ground for how to do a package manager 4 | for Scryer Prolog. This is still really rough (currently it has trivially 5 | exploitable arbitrary remote code execution) and is not intended for general 6 | use yet. If you want to contribute or have any ideas or questions feel free to 7 | get in touch and create issues, pull requests and 8 | [discussions](https://github.com/bakaq/bakage/discussions). 9 | 10 | ## How to use 11 | 12 | A package is a directory with a `scryer-manifest.pl` following this schema: 13 | 14 | ```prolog 15 | name("name_of_the_package"). 16 | % Optional. The file that will be imported when this package is used. 17 | main_file("main.pl"). 18 | % The license of the package 19 | license(name("Unlicense"), path("./UNLICENSE")). 20 | % Optional 21 | dependencies([ 22 | % A git url to clone 23 | dependency("testing", git("https://github.com/bakaq/testing.pl.git")), 24 | % A git url to clone at a specific branch 25 | dependency("test_branch", git("https://github.com/constraintAutomaton/test-prolog-package-manager.git", branch("branch"))), 26 | % A git url to clone at a tag 27 | dependency("test_tag", git("https://github.com/constraintAutomaton/test-prolog-package-manager.git", tag("tag"))), 28 | % A git url to clone at a specific commit hash 29 | dependency("test_hash", git("https://github.com/constraintAutomaton/test-prolog-package-manager.git", hash("d19fefc1d7907f6675e181601bb9b8b94561b441"))), 30 | % A path to a local package 31 | dependency("test_local", path("./local_package")) 32 | ]). 33 | 34 | ``` 35 | 36 | Copy the `bakage.pl` file [from the 37 | releases](https://github.com/bakaq/bakage/releases) into your project. It is 38 | both the dependency manager and the package loader. Use `./bakage.pl install` 39 | to download the dependencies to a `scryer_libs` directory (it doesn't handle 40 | transitive dependencies yet). You can then import packages in your code as 41 | follows: 42 | 43 | ```prolog 44 | % Loads the package loader 45 | :- use_module(bakage). 46 | 47 | % Loads a package. The argument should be an atom equal to the name of the 48 | % dependency package specified in the `name/1` field of its manifest. 49 | :- use_module(pkg(testing)). 50 | 51 | % You can then use the predicates exported by the main file of the dependency 52 | % in the rest of the program. 53 | 54 | main :- 55 | % `run_tests/0` is exported by `pkg(testing)` 56 | run_tests, 57 | halt. 58 | 59 | test("Example test", (true)). 60 | test("Another test", (false)). 61 | ``` 62 | 63 | ## Contributing 64 | 65 | See [CONTRIBUTING.md](CONTRIBUTING.md). 66 | -------------------------------------------------------------------------------- /tests/integration/manifest_multiple_dependencies/justfile: -------------------------------------------------------------------------------- 1 | test: 2 | #!/bin/sh 3 | set -eu 4 | 5 | . ../../utils/report_test.sh 6 | 7 | printf "%s\n" "Testing installation of multiple dependencies" 8 | 9 | rm -rf scryer_libs && rm -f manifest-lock.pl 10 | 11 | scryer-prolog main.pl -g "catch(main, _, halt(1)) ." 12 | 13 | exitCode=0 14 | 15 | if [ ! -d "./scryer_libs/packages/test_branch" ]; then 16 | exitCode=1 17 | print_test_result "the package folder exist for the dependency test_branch" failure 18 | else 19 | print_test_result "the package folder exist for the dependency test_branch" success 20 | fi 21 | 22 | if [ ! -f "./scryer_libs/packages/test_branch/scryer-manifest.pl" ]; then 23 | exitCode=1 24 | print_test_result "the manifest of the dependency test_branch exist" failure 25 | else 26 | print_test_result "the manifest of the dependency test_branch exist" success 27 | fi 28 | 29 | dependencyBranch=$(cd ./scryer_libs/packages/test_branch && git rev-parse --abbrev-ref HEAD) 30 | 31 | if [ "$dependencyBranch" != "branch" ]; then 32 | exitCode=1 33 | print_test_result "the dependency test_branch is at the correct git branch" failure 34 | else 35 | print_test_result "the dependency test_branch is at the correct git branch" success 36 | fi 37 | 38 | if [ ! -d "./scryer_libs/packages/test" ]; then 39 | exitCode=1 40 | print_test_result "the package folder exist for the dependency test" failure 41 | else 42 | print_test_result "the package folder exist for the dependencies test" success 43 | fi 44 | 45 | if [ ! -f "./scryer_libs/packages/test/scryer-manifest.pl" ]; then 46 | exitCode=1 47 | print_test_result "the manifest of the dependency test exist" failure 48 | else 49 | print_test_result "the manifest of the dependency test exist" success 50 | fi 51 | 52 | dependencyBranch=$(cd ./scryer_libs/packages/test && git rev-parse --abbrev-ref HEAD) 53 | 54 | if [ "$dependencyBranch" != "main" ]; then 55 | exitCode=1 56 | print_test_result "the dependency test is at the correct git branch" failure 57 | else 58 | print_test_result "the dependency test is at the correct git branch" success 59 | fi 60 | 61 | 62 | if [ ! -d "./scryer_libs/packages/test_hash" ]; then 63 | exitCode=1 64 | print_test_result "the package folder exist for the dependency test_hash" failure 65 | else 66 | print_test_result "the package folder exist for the dependency test_hash" success 67 | fi 68 | 69 | if [ ! -f "./scryer_libs/packages/test_hash/scryer-manifest.pl" ]; then 70 | exitCode=1 71 | print_test_result "the manifest of the dependency test_hash exist" failure 72 | else 73 | print_test_result "the manifest of the dependency test_hash exist" success 74 | fi 75 | 76 | dependencyGitHash=$(cd ./scryer_libs/packages/test_hash && git rev-parse HEAD) 77 | 78 | if [ "$dependencyGitHash" != "d19fefc1d7907f6675e181601bb9b8b94561b441" ]; then 79 | exitCode=1 80 | print_test_result "the dependency test_hash is at the correct git commit hash" failure 81 | else 82 | print_test_result "the dependency test_hash is at the correct git commit hash" success 83 | fi 84 | 85 | 86 | if [ ! -d "./scryer_libs/packages/test_tag" ]; then 87 | exitCode=1 88 | print_test_result "the package folder exist for the dependency test_tag" failure 89 | else 90 | print_test_result "the package folder exist for the dependency test_tag" success 91 | fi 92 | 93 | if [ ! -f "./scryer_libs/packages/test_tag/scryer-manifest.pl" ]; then 94 | exitCode=1 95 | print_test_result "the manifest of the dependency exist" failure 96 | else 97 | print_test_result "the manifest of the dependency exist" success 98 | fi 99 | 100 | dependencyTag=$(cd ./scryer_libs/packages/test_tag && git describe --exact-match --tags 2>/dev/null) 101 | 102 | if [ "$dependencyTag" != "tag" ]; then 103 | exitCode=1 104 | print_test_result "the dependency test_tag is at the correct git tag" failure 105 | else 106 | print_test_result "the dependency test_tag is at the correct git tag" success 107 | fi 108 | 109 | if [ ! -d "./scryer_libs/packages/test_local" ]; then 110 | exitCode=1 111 | print_test_result "the package folder exist for the dependency test_local" failure 112 | else 113 | print_test_result "the package folder exist for the dependency test_local" success 114 | fi 115 | 116 | if [ ! -f "./scryer_libs/packages/test_local/scryer-manifest.pl" ]; then 117 | exitCode=1 118 | print_test_result "the manifest of the dependency test_local exist" failure 119 | else 120 | print_test_result "the manifest of the dependency test_local exist" success 121 | fi 122 | rm -rf scryer_libs && rm -f manifest-lock.pl 123 | 124 | exit ${exitCode} -------------------------------------------------------------------------------- /tests/integration/manifest_multiple_dependencies_with_one_error/justfile: -------------------------------------------------------------------------------- 1 | test: 2 | #!/bin/sh 3 | set -eu 4 | 5 | . ../../utils/report_test.sh 6 | 7 | printf "%s\n" "Testing installation of multiple dependencies with one error" 8 | 9 | rm -rf scryer_libs && rm -f manifest-lock.pl 10 | 11 | scryer-prolog main.pl -g "catch(main, _, halt(1)) ." 12 | 13 | exitCode=0 14 | 15 | if [ ! -d "./scryer_libs/packages/test_branch" ]; then 16 | exitCode=1 17 | print_test_result "the package folder exist for the dependency test_branch" failure 18 | else 19 | print_test_result "the package folder exist for the dependency test_branch" success 20 | fi 21 | 22 | if [ ! -f "./scryer_libs/packages/test_branch/scryer-manifest.pl" ]; then 23 | exitCode=1 24 | print_test_result "the manifest of the dependency test_branch exist" failure 25 | else 26 | print_test_result "the manifest of the dependency test_branch exist" success 27 | fi 28 | 29 | dependencyBranch=$(cd ./scryer_libs/packages/test_branch && git rev-parse --abbrev-ref HEAD) 30 | 31 | if [ "$dependencyBranch" != "branch" ]; then 32 | exitCode=1 33 | print_test_result "the dependency test_branch is at the correct git branch" failure 34 | else 35 | print_test_result "the dependency test_branch is at the correct git branch" success 36 | fi 37 | 38 | if [ ! -d "./scryer_libs/packages/test" ]; then 39 | exitCode=1 40 | print_test_result "the package folder exist for the dependency test" failure 41 | else 42 | print_test_result "the package folder exist for the dependencies test" success 43 | fi 44 | 45 | if [ ! -f "./scryer_libs/packages/test/scryer-manifest.pl" ]; then 46 | exitCode=1 47 | print_test_result "the manifest of the dependency test exist" failure 48 | else 49 | print_test_result "the manifest of the dependency test exist" success 50 | fi 51 | 52 | dependencyBranch=$(cd ./scryer_libs/packages/test && git rev-parse --abbrev-ref HEAD) 53 | 54 | if [ "$dependencyBranch" != "main" ]; then 55 | exitCode=1 56 | print_test_result "the dependency test is at the correct git branch" failure 57 | else 58 | print_test_result "the dependency test is at the correct git branch" success 59 | fi 60 | 61 | 62 | if [ ! -d "./scryer_libs/packages/test_hash" ]; then 63 | exitCode=1 64 | print_test_result "the package folder exist for the dependency test_hash" failure 65 | else 66 | print_test_result "the package folder exist for the dependency test_hash" success 67 | fi 68 | 69 | if [ ! -f "./scryer_libs/packages/test_hash/scryer-manifest.pl" ]; then 70 | exitCode=1 71 | print_test_result "the manifest of the dependency test_hash exist" failure 72 | else 73 | print_test_result "the manifest of the dependency test_hash exist" success 74 | fi 75 | 76 | dependencyGitHash=$(cd ./scryer_libs/packages/test_hash && git rev-parse HEAD) 77 | 78 | if [ "$dependencyGitHash" != "d19fefc1d7907f6675e181601bb9b8b94561b441" ]; then 79 | exitCode=1 80 | print_test_result "the dependency test_hash is at the correct git commit hash" failure 81 | else 82 | print_test_result "the dependency test_hash is at the correct git commit hash" success 83 | fi 84 | 85 | 86 | if [ ! -d "./scryer_libs/packages/test_tag" ]; then 87 | exitCode=1 88 | print_test_result "the package folder exist for the dependency test_tag" failure 89 | else 90 | print_test_result "the package folder exist for the dependency test_tag" success 91 | fi 92 | 93 | if [ ! -f "./scryer_libs/packages/test_tag/scryer-manifest.pl" ]; then 94 | exitCode=1 95 | print_test_result "the manifest of the dependency exist" failure 96 | else 97 | print_test_result "the manifest of the dependency exist" success 98 | fi 99 | 100 | dependencyTag=$(cd ./scryer_libs/packages/test_tag && git describe --exact-match --tags 2>/dev/null) 101 | 102 | if [ "$dependencyTag" != "tag" ]; then 103 | exitCode=1 104 | print_test_result "the dependency test_tag is at the correct git tag" failure 105 | else 106 | print_test_result "the dependency test_tag is at the correct git tag" success 107 | fi 108 | 109 | if [ ! -d "./scryer_libs/packages/test_local" ]; then 110 | exitCode=1 111 | print_test_result "the package folder exist for the dependency test_local" failure 112 | else 113 | print_test_result "the package folder exist for the dependency test_local" success 114 | fi 115 | 116 | if [ ! -f "./scryer_libs/packages/test_local/scryer-manifest.pl" ]; then 117 | exitCode=1 118 | print_test_result "the manifest of the dependency test_local exist" failure 119 | else 120 | print_test_result "the manifest of the dependency test_local exist" success 121 | fi 122 | rm -rf scryer_libs && rm -f manifest-lock.pl 123 | 124 | exit ${exitCode} -------------------------------------------------------------------------------- /scripts/ensure_dependencies.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -u 3 | 4 | # Fail instead of prompting for password in git commands. 5 | export GIT_TERMINAL_PROMPT=0 6 | 7 | write_result() { 8 | flock scryer_libs/temp/install_resp.pl.lock -c \ 9 | "printf 'result(\"%s\", %s).\n' \"$1\" \"$2\" >> scryer_libs/temp/install_resp.pl" 10 | } 11 | 12 | write_success() { 13 | write_result "$1" "success" 14 | } 15 | 16 | write_error() { 17 | escaped_error=$(printf '%s' "$2" | sed -e 's/\\/\\\\/g' -e 's/"/\\"/g') 18 | escaped_error=$(printf '%s' "$escaped_error" | tr '\r\n' '\\n') 19 | escaped_error=$(printf '%s' "$escaped_error" | sed 's/ / /g') 20 | write_result "$1" "error(\\\"$escaped_error\\\")" 21 | } 22 | 23 | install_git_default() { 24 | dependency_name=$1 25 | git_url=$2 26 | 27 | error_output=$( 28 | git clone \ 29 | --quiet \ 30 | --depth 1 \ 31 | --single-branch \ 32 | "${git_url}" \ 33 | "scryer_libs/packages/${dependency_name}" 2>&1 1>/dev/null 34 | ) 35 | 36 | if [ -z "$error_output" ]; then 37 | write_success "${dependency_name}" 38 | else 39 | write_error "${dependency_name}" "$error_output" 40 | fi 41 | } 42 | 43 | install_git_branch() { 44 | dependency_name=$1 45 | git_url=$2 46 | git_branch=$3 47 | 48 | error_output=$( 49 | git clone \ 50 | --quiet \ 51 | --depth 1 \ 52 | --single-branch \ 53 | --branch "${git_branch}" \ 54 | "${git_url}" \ 55 | "scryer_libs/packages/${dependency_name}" 2>&1 1>/dev/null 56 | ) 57 | 58 | if [ -z "$error_output" ]; then 59 | write_success "${dependency_name}" 60 | else 61 | write_error "${dependency_name}" "$error_output" 62 | fi 63 | } 64 | 65 | install_git_tag() { 66 | dependency_name=$1 67 | git_url=$2 68 | git_tag=$3 69 | 70 | error_output=$( 71 | git clone \ 72 | --quiet \ 73 | --depth 1 \ 74 | --single-branch \ 75 | --branch "${git_tag}" \ 76 | "${git_url}" \ 77 | "scryer_libs/packages/${dependency_name}" 2>&1 1>/dev/null 78 | ) 79 | 80 | if [ -z "$error_output" ]; then 81 | write_success "${dependency_name}" 82 | else 83 | write_error "${dependency_name}" "$error_output" 84 | fi 85 | } 86 | 87 | install_git_hash() { 88 | dependency_name=$1 89 | git_url=$2 90 | git_hash=$3 91 | 92 | error_output=$( 93 | git clone \ 94 | --quiet \ 95 | --depth 1 \ 96 | --single-branch \ 97 | "${git_url}" \ 98 | "scryer_libs/packages/${dependency_name}" 2>&1 1>/dev/null 99 | ) 100 | 101 | if [ -z "$error_output" ]; then 102 | fetch_error=$( 103 | git -C "scryer_libs/packages/${dependency_name}" fetch \ 104 | --quiet \ 105 | --depth 1 \ 106 | origin "${git_hash}" 2>&1 1>/dev/null 107 | ) 108 | switch_error=$( 109 | git -C "scryer_libs/packages/${dependency_name}" switch \ 110 | --quiet \ 111 | --detach \ 112 | "${git_hash}" 2>&1 1>/dev/null 113 | ) 114 | combined_error="${fetch_error}; ${switch_error}" 115 | 116 | if [ -z "$fetch_error" ] && [ -z "$switch_error" ]; then 117 | write_success "${dependency_name}" 118 | else 119 | write_error "${dependency_name}" "$combined_error" 120 | fi 121 | else 122 | write_error "${dependency_name}" "$error_output" 123 | fi 124 | } 125 | 126 | install_path() { 127 | dependency_name=$1 128 | dependency_path=$2 129 | 130 | if [ -d "${dependency_path}" ]; then 131 | error_output=$(ln -rsf "${dependency_path}" "scryer_libs/packages/${dependency_name}" 2>&1 1>/dev/null) 132 | 133 | if [ -z "$error_output" ]; then 134 | write_success "${dependency_name}" 135 | else 136 | write_error "${dependency_name}" "$error_output" 137 | fi 138 | else 139 | write_error "${dependency_name}" "${dependency_path} does not exist" 140 | fi 141 | } 142 | 143 | OLD_IFS=$IFS 144 | IFS='|' 145 | set -- $DEPENDENCIES_STRING 146 | IFS=$OLD_IFS 147 | 148 | touch scryer_libs/temp/install_resp.pl 149 | 150 | for dependency in "$@"; do 151 | unset dependency_term dependency_kind dependency_name git_url git_branch git_tag git_hash dependency_path 152 | 153 | IFS=';' 154 | set -- $dependency 155 | IFS=$OLD_IFS 156 | 157 | while [ "$#" -gt 0 ]; do 158 | field=$1 159 | shift 160 | 161 | key=$(printf "%s" "$field" | cut -d= -f1) 162 | value=$(printf "%s" "$field" | cut -d= -f2-) 163 | 164 | case "$key" in 165 | dependency_term) dependency_term=$value ;; 166 | dependency_kind) dependency_kind=$value ;; 167 | dependency_name) dependency_name=$value ;; 168 | git_url) git_url=$value ;; 169 | git_branch) git_branch=$value ;; 170 | git_tag) git_tag=$value ;; 171 | git_hash) git_hash=$value ;; 172 | dependency_path) dependency_path=$value ;; 173 | esac 174 | done 175 | 176 | printf "Ensuring is installed: %s\n" "${dependency_term}" 177 | 178 | case "${dependency_kind}" in 179 | do_nothing) ;; 180 | 181 | git_default) 182 | install_git_default "${dependency_name}" "${git_url}" & 183 | ;; 184 | git_branch) 185 | install_git_branch "${dependency_name}" "${git_url}" "${git_branch}" & 186 | ;; 187 | git_tag) 188 | install_git_tag "${dependency_name}" "${git_url}" "${git_tag}" & 189 | ;; 190 | git_hash) 191 | install_git_hash "${dependency_name}" "${git_url}" "${git_hash}" & 192 | ;; 193 | path) 194 | install_path "${dependency_name}" "${dependency_path}" & 195 | ;; 196 | *) 197 | printf "Unknown dependency kind: %s\n" "${dependency_kind}" 198 | write_error "${dependency_name}" "Unknown dependency kind: ${dependency_kind}" 199 | ;; 200 | esac 201 | done 202 | 203 | wait 204 | 205 | rm -f scryer_libs/temp/install_resp.pl.lock 206 | -------------------------------------------------------------------------------- /tests/utils/testing.pl: -------------------------------------------------------------------------------- 1 | /** Testing framework 2 | 3 | This module provides the predicates `run_tests/0` and `run_tests/1` that can be used to do basic 4 | unit testing. 5 | 6 | # Getting started 7 | 8 | Suppose you have a file `my_module.pl` with contents: 9 | 10 | ```prolog 11 | :- module(my_module, [example/1]). 12 | 13 | example(1). 14 | example(2). 15 | ``` 16 | 17 | You could then write a `my_module_tests.pl` with contents: 18 | 19 | ```prolog 20 | :- use_module(testing). 21 | 22 | :- use_module(my_module). 23 | 24 | test("test 1 and 2", ( 25 | example(1), 26 | example(2) 27 | )). 28 | 29 | test("test 3 and 4", ( 30 | example(3), 31 | example(4) 32 | )). 33 | ``` 34 | 35 | You can then run the tests with the following command: 36 | 37 | ``` 38 | $ scryer-prolog -f my_module_tests.pl -g run_tests 39 | Running tests in module user. 40 | test "test 1 and 2" ... succeeded 41 | test "test 3 and 4" ... failed 42 | ``` 43 | 44 | And we can see that our test failed. We can filter just the failing test to investigate it better. 45 | 46 | ``` 47 | $ scryer-prolog -f my_module_tests.pl -g 'run_tests([filter("test 3 and 4")])' 48 | Running tests in module user. 49 | test "test 3 and 4" ... failed 50 | ``` 51 | 52 | Adding `example(3). example(4).` to `my_module.pl` and rerunning the tests we see that it now 53 | passes. 54 | 55 | # How it works 56 | 57 | This testing framework expects the tests to be written as predicates of the form 58 | `test(-Name, -Goal)`. `Name` is the name of the test that can be used to identify and filter it, 59 | `Goal` is the goal that needs to succeed for the test to pass. 60 | 61 | By default `run_tests/1` only searches the `user` module for these test predicates, and so only 62 | finds the tests that are written outside any module. You can use the `modules(+Modules)` option 63 | to specify other modules it should search. This making it possible to write unit tests inside 64 | modules, although this may lead to unexpected behavior because of limitations of the module system 65 | and so it's not very recomended if you need to deal with metapredicates. 66 | 67 | Errors and other exceptions make a test fail, but are reported to the user to help in debugging: 68 | 69 | ``` 70 | $ scryer-prolog -f my_module_tests.pl -g 'run_tests' 71 | Running tests in module user. 72 | test "example with error" ... error(instantiation_error,functor/3) 73 | test "example with exception" ... exception(example_exception) 74 | ``` 75 | 76 | After running, `run_tests/1` exits with a status code of 0 if all tests that were run succeeded and 77 | 1 otherwise. This makes it possible to check if tests pass inside scripts. 78 | */ 79 | 80 | % SPDX-License-Identifier: Unlicense 81 | 82 | :- module(testing, [run_tests/0, run_tests/1]). 83 | 84 | :- use_module(library(lists)). 85 | :- use_module(library(dcgs)). 86 | :- use_module(library(pio)). 87 | :- use_module(library(format)). 88 | :- use_module(library(lambda)). 89 | :- use_module(library(error)). 90 | 91 | %% run_tests. 92 | % 93 | % Runs tests with default options. See `run_tests/1`. 94 | run_tests :- run_tests([]). 95 | 96 | %% run_tests(+Options). 97 | % 98 | % Runs tests with the options given in the list `Options`. Currently supported are: 99 | % 100 | % - `modules(+Modules)`: Runs the tests found in the given modules. Default: `[user]`. 101 | % - `filter(+Filter)`: Either `no_filter` to do no filtering or a string. Runs only the tests that 102 | % contain the string in their names. Default: `no_filter`. 103 | % - `color(+Color)`: Either `true` or `false` to indicate if the output should be colored or not. 104 | % Default: `true`. 105 | % - `halt(+Halt)`: Either `true` or `false` to indicate if we should halt after the tests. 106 | % If this is set to `false` and any of the tests fail, then `run_tests/1` fails. Default: `true`. 107 | run_tests(Options) :- 108 | must_be(list, Options), 109 | options_option_default(Options, modules(Modules), [user]), 110 | options_option_default(Options, filter(Filter), no_filter), 111 | options_option_default(Options, color(Color), true), 112 | options_option_default(Options, halt(Halt), true), 113 | run_tests_opt(Modules, Color, Filter, Halt). 114 | 115 | run_tests_opt(Modules, Color, Filter, Halt) :- 116 | maplist(module_mtests, Modules, MTests), 117 | maplist( 118 | [Color,Filter]+\(Module-Tests)^Succ^run_tests_module_opt(Module, Tests, Color, Filter, Succ), 119 | MTests, 120 | Successes 121 | ), 122 | ( all_succeeded(Successes) -> 123 | ( Halt == true -> 124 | halt 125 | ; true 126 | ) 127 | ; ( Halt == true -> 128 | halt(1) 129 | ; false 130 | ) 131 | ). 132 | 133 | module_tests(Module, Tests) :- 134 | catch( 135 | findall( 136 | test(Name, Module:Goal), 137 | ( 138 | % Workaround. See: https://github.com/mthom/scryer-prolog/issues/2826 139 | G0 = Module:test(Name, Goal), 140 | call(G0) 141 | ), 142 | Tests 143 | ), 144 | error(existence_error(procedure,_),_), 145 | Tests = [] 146 | ), 147 | true. 148 | 149 | module_mtests(Module, MTests) :- 150 | module_tests(Module, Tests), 151 | MTests = Module-Tests. 152 | 153 | all_succeeded([]). 154 | all_succeeded([true|Succs]) :- all_succeeded(Succs). 155 | 156 | run_tests_module_opt(Module, Tests0, Color, Filter, Success) :- 157 | filter_tests(Filter, Tests0, Tests), 158 | portray(( 159 | "Running tests in module ", 160 | ansi(Color, white), format_("~q", [Module]), ansi(Color, reset), 161 | ".\n" 162 | )), 163 | run_tests_(Tests, Color, true, Success). 164 | 165 | options_option_default(Options, Option, Default) :- 166 | ( member(Option, Options) -> 167 | true 168 | ; Option =.. [_, Default] 169 | ). 170 | 171 | run_tests_([], _, Success, Success). 172 | run_tests_([test(Name, Goal)|Tests], Color, Success0, Success) :- 173 | portray(format_(" test \"~s\" ... ", [Name])), 174 | ( catch(call(Goal), Exception, true) -> 175 | ( nonvar(Exception) -> 176 | portray(ansi(Color, red)), 177 | ( Exception = error(_,_) -> 178 | portray(format_("~q", [Exception])) 179 | ; portray(format_("exception(~q)", [Exception])) 180 | ), 181 | portray(ansi(Color, reset)), 182 | Success1 = false 183 | ; portray((ansi(Color, green), "succeeded", ansi(Color, reset))), 184 | Success1 = Success0 185 | ) 186 | ; portray((ansi(Color, red), "failed", ansi(Color, reset))), 187 | Success1 = false 188 | ), 189 | portray("\n"), 190 | run_tests_(Tests, Color, Success1, Success). 191 | 192 | ansi(true, reset) --> "\x1b\[0m". 193 | ansi(true, red) --> "\x1b\[31;1m". 194 | ansi(true, green) --> "\x1b\[32;1m". 195 | ansi(true, white) --> "\x1b\[37;1m". 196 | ansi(false, _) --> []. 197 | 198 | portray(GRBody) :- 199 | phrase_to_stream(GRBody, user_output). 200 | 201 | filter_tests(Filter, Tests0, Tests) :- 202 | ( Filter == no_filter -> 203 | Tests = Tests0 204 | ; phrase(filter_tests_(Tests0, Filter), Tests) 205 | ). 206 | 207 | filter_tests_([], _) --> []. 208 | filter_tests_([test(Name, Body)|Tests], Filter) --> 209 | ( { phrase((..., Filter, ...), Name) } -> 210 | [test(Name, Body)] 211 | ; [] 212 | ), 213 | filter_tests_(Tests, Filter). 214 | -------------------------------------------------------------------------------- /src/qupak.pl: -------------------------------------------------------------------------------- 1 | % Qupak: Pattern Matching for library(reif) 2 | % 3 | % === Bibliographic Note === 4 | % Author: Kauê Hunnicutt Bazilli (https://github.com/bakaq) 5 | % Original implementation: https://github.com/bakaq/qupak 6 | % Git Commit Hash: 5ca7fc6ea82c74fb71a2204a8c43f16e97e14d8f 7 | % 8 | % Notes: 9 | % - This version removes operators (e.g., ~>, |) for predicates 10 | % to avoid shipping them in every project using Bakage. 11 | % 12 | % This library provides predicates to do pattern matching in a way that complements and 13 | % expands on library(reif). 14 | % 15 | % Tested on Scryer Prolog 0.10. 16 | % 17 | % === Pattern matching === 18 | % 19 | % The core predicate of this library is pattern_match_t/3. It has a non-reified version 20 | % pattern_match/2, along with predicate aliases pattern_match_rev/2-3 (value first order). 21 | % 22 | % The pattern consists of a term that is a clean representation of what you want to match. 23 | % You always need to specify how a term will be matched with a wrapping functor. 24 | 25 | % --- ground: Ground term matching --- 26 | % 27 | % Matches a ground term. Doesn't need to be atomic. 28 | % 29 | % Examples: 30 | % ?- pattern_match_rev(a(1,2,3), ground(a(1,2,3))). 31 | % true. 32 | % ?- pattern_match_rev(a(1,2,3), ground(a(1,2,3)), T). 33 | % T = true. 34 | 35 | % --- bind: Bind variable --- 36 | % 37 | % Binds a variable. This does normal unification, not reified unification like check. 38 | % 39 | % Examples: 40 | % ?- pattern_match_rev(a(1,2,3), bind(X)). 41 | % X = a(1,2,3). 42 | % ?- pattern_match_rev(a(1,2,3), bind(X), T). 43 | % X = a(1,2,3), T = true. 44 | 45 | % --- check: Reified unification --- 46 | % 47 | % Does reified unification. If used in a non-reified predicate, it's basically the same 48 | % thing as bind, but creates a spurious choice point. When used in a reified 49 | % predicate, does the unification like =/3, exploring the dif/2 case on backtracking. 50 | % 51 | % Examples: 52 | % ?- pattern_match_rev(a(1,2,3), check(X)). 53 | % X = a(1,2,3) 54 | % ; false. 55 | % ?- pattern_match_rev(a(1,2,3), check(X), T). 56 | % X = a(1,2,3), T = true 57 | % ; T = false, dif:dif(X,a(1,2,3)). 58 | 59 | % --- any: Wildcard --- 60 | % 61 | % Matches anything, and ignores it. 62 | % 63 | % Examples: 64 | % ?- pattern_match_rev(a(1,2,3), any). 65 | % true. 66 | % ?- pattern_match_rev(a(1,2,3), any, T). 67 | % T = true. 68 | 69 | % --- composite: Compound term --- 70 | % 71 | % Matches a compound term, applying patterns recursively. If the compound term is ground, 72 | % you can use ground instead. 73 | % 74 | % Examples: 75 | % ?- pattern_match_rev(a(1,2,3,4), composite(a(ground(1), bind(A), check(B), any))). 76 | % A = 2, B = 3 77 | % ; false. 78 | % ?- pattern_match_rev(a(1,2,3,4), composite(a(ground(1), bind(A), check(B), any)), T). 79 | % A = 2, B = 3, T = true 80 | % ; A = 2, T = false, dif:dif(B,3). 81 | 82 | % --- bind_all: All binding --- 83 | % 84 | % Binds the whole pattern to a variable. It can be used at any nesting level, not just 85 | % at the top level. 86 | % 87 | % Examples: 88 | % ?- pattern_match_rev(a(1,2,3,4), bind_all(All, composite(a(ground(1), bind(A), check(B), any)))). 89 | % All = a(1,2,3,4), A = 2, B = 3 90 | % ; false. 91 | % ?- pattern_match_rev(a(1,2,3,4), bind_all(All, composite(a(ground(1), bind(A), check(B), any))), T). 92 | % All = a(1,2,3,4), A = 2, B = 3, T = true 93 | % ; All = a(1,2,3,4), A = 2, T = false, dif:dif(B,3). 94 | 95 | % --- guard: Guards --- 96 | % 97 | % Runs a reified predicate after the bindings to decide if the pattern matches. 98 | % 99 | % Examples: 100 | % ?- use_module(library(clpz)). 101 | % true. 102 | % ?- pattern_match_rev(a(50), guard(composite(a(bind(N))), clpz_t(N #< 25))). 103 | % false. 104 | % ?- pattern_match_rev(a(10), guard(composite(a(bind(N))), clpz_t(N #< 25))). 105 | % N = 10. 106 | % ?- pattern_match_rev(a(50), guard(composite(a(bind(N))), clpz_t(N #< 25)), T). 107 | % N = 50, T = false. 108 | % ?- pattern_match_rev(a(10), guard(composite(a(bind(N))), clpz_t(N #< 25)), T). 109 | % N = 10, T = true. 110 | 111 | % === match/2: Reified pattern matching switch === 112 | % 113 | % The match/2 predicate implements something like a switch/match statement on top of 114 | % if_/3 and the pattern matching predicates from this library. The arms are matched in 115 | % order, and the first one to succeed is the one taken. 116 | % 117 | % When a check pattern backtracks and tries the dif/2 case, it will test the next 118 | % arms in the match/2. The dif/2 constraint will be available for all the arms 119 | % from there on. 120 | % 121 | % Examples: 122 | % example_match(Term, Out) :- 123 | % match(Term, [ 124 | % arm(ground(a), (Out = 1)), 125 | % arm(composite(b(bind(N))), (Out = N)), 126 | % arm(composite(c(check(N))), (Out = N)), 127 | % arm(any, (Out = wildcard)) 128 | % ]). 129 | % 130 | % ?- example_match(a, Out). 131 | % Out = 1. 132 | % ?- example_match(b(10), Out). 133 | % Out = 10. 134 | % ?- example_match(c(10), Out). 135 | % Out = 10 136 | % ; Out = wildcard, dif:dif(_A,10). 137 | 138 | % === Migration from operator version === 139 | % 140 | % Old operator syntax -> New predicate syntax: 141 | % +Term -> ground(Term) 142 | % -Var -> bind(Var) 143 | % ?Var -> check(Var) 144 | % * -> any 145 | % \Structure -> composite(Structure) 146 | % All << Pattern -> bind_all(All, Pattern) 147 | % Pattern | Guard -> guard(Pattern, Guard) 148 | % Pattern ~> Goal -> arm(Pattern, Goal) 149 | % Value =~ Pattern -> pattern_match_rev(Value, Pattern) 150 | % Pattern ~= Value -> pattern_match(Pattern, Value) 151 | 152 | :- use_module(library(reif)). 153 | :- use_module(library(lists)). 154 | :- use_module(library(dcgs)). 155 | 156 | % === Helper predicates === 157 | 158 | and(true, true, true). 159 | and(true, false, false). 160 | and(false, true, false). 161 | and(false, false, false). 162 | 163 | %% pattern_match_rev(Value, Pattern) - emulates Value =~ Pattern 164 | pattern_match_rev(Value, Pattern, T) :- 165 | pattern_match_t(Pattern, Value, T). 166 | pattern_match_rev(Value, Pattern) :- 167 | pattern_match_t(Pattern, Value, true). 168 | 169 | %% pattern_match(Pattern, Value) - emulates Pattern ~= Value 170 | pattern_match(Pattern, Value, T) :- 171 | pattern_match_t(Pattern, Value, T). 172 | pattern_match(Pattern, Value) :- 173 | pattern_match_t(Pattern, Value, true). 174 | 175 | %% pattern_match_t(Pattern, Value, T). 176 | % 177 | % A predicate to check if a value matches a certain pattern. 178 | % The pattern is a specially constructed term that describes 179 | % a pattern so that this can be monotonic. 180 | pattern_match_t(Pattern, Value, T) :- 181 | % TODO: Error handling of pattern arity. 182 | Pattern =.. [PatternKind|PatternArgs], 183 | inner_pattern_match(PatternKind, PatternArgs, Value, T). 184 | 185 | % Pattern constructors - emulate the symbol operators 186 | % any - emulates * 187 | inner_pattern_match(any, [], _, true). 188 | 189 | % bind - emulates - 190 | inner_pattern_match(bind, [Variable], Value, true) :- 191 | Variable = Value. 192 | 193 | % check - emulates ? 194 | inner_pattern_match(check, [Variable], Value, T) :- 195 | =(Variable, Value, T). 196 | 197 | % ground - emulates + 198 | inner_pattern_match(ground, [Ground], Value, T) :- 199 | % TODO: Error handling 200 | ground(Ground), 201 | =(Ground, Value, T). 202 | 203 | % composite - emulates \ 204 | inner_pattern_match(composite, [Composite], Value, T) :- 205 | Composite =.. [Functor|Args], 206 | length(Args, Arity), 207 | functor(Value, ValueFunctor, ValueArity), 208 | if_( 209 | Functor = ValueFunctor, 210 | if_( 211 | Arity = ValueArity, 212 | ( 213 | Value =.. [_|ValueArgs], 214 | pattern_match_arguments(Args, ValueArgs, true, T) 215 | ), 216 | T = false 217 | ), 218 | T = false 219 | ). 220 | 221 | % bind_all - emulates 222 | inner_pattern_match(bind_all, [Variable, Pattern], Value, T) :- 223 | Variable = Value, 224 | pattern_match_t(Pattern, Value, T). 225 | 226 | % guard - emulates | in patterns 227 | inner_pattern_match(guard, [Pattern, Guard_1], Value, T) :- 228 | if_( 229 | pattern_match_t(Pattern, Value), 230 | call(Guard_1, T), 231 | T = false 232 | ). 233 | 234 | pattern_match_arguments([], [], T, T). 235 | pattern_match_arguments([Pattern|Patterns], [Arg|Args], T0, T) :- 236 | pattern_match_t(Pattern, Arg, T1), 237 | and(T0, T1, T2), 238 | pattern_match_arguments(Patterns, Args, T2, T). 239 | 240 | % === match/2 predicate === 241 | 242 | %% match(Term, Arms) 243 | % Pattern matching with multiple arms. 244 | % Arms should be a list of arm(Pattern, Goal) terms 245 | match(Term,Arms) :- 246 | match_run(Arms, Term). 247 | 248 | match_run([], _) :- false. 249 | match_run([arm(Pattern, Goal_0)|Arms], Term) :- 250 | if_( 251 | pattern_match_rev(Term, Pattern), 252 | call(Goal_0), 253 | match_run(Arms, Term) 254 | ). 255 | -------------------------------------------------------------------------------- /src/cli.pl: -------------------------------------------------------------------------------- 1 | run :- 2 | ( 3 | catch( 4 | main, 5 | Error, 6 | ( 7 | portray_clause(Error), 8 | halt(1) 9 | ) 10 | ), 11 | halt 12 | ; phrase_to_stream("main predicate failed", user_output), 13 | halt(1) 14 | ). 15 | 16 | main :- 17 | collect_relevant_env_vars(EnvVars), 18 | argv(Args), 19 | parse_args(Args, ParsedArgs), 20 | resolve_flags_and_task(ParsedArgs, EnvVars, GlobalFlags, Task), 21 | set_global_state(GlobalFlags), 22 | do_task(Task, GlobalFlags). 23 | 24 | parse_args(Args, ParsedArgs) :- 25 | phrase(chunked_args(ChunkedArgs), Args), 26 | parse_chunked_args(ChunkedArgs, parsed_args([], []), ParsedArgs). 27 | 28 | parse_chunked_args([], ParsedArgs, ParsedArgs). 29 | parse_chunked_args([CA|CAs], ParsedArgs0, ParsedArgs) :- 30 | parse_chunked_arg(CA, ParsedArgs0, ParsedArgs1), 31 | parse_chunked_args(CAs, ParsedArgs1, ParsedArgs). 32 | 33 | parse_chunked_arg(flag(Flag), parsed_args(Command, Flags0), parsed_args(Command, Flags)) :- 34 | options_upsert(Flag, Flags0, Flags). 35 | 36 | parse_chunked_arg(command(Cmd), parsed_args(Command0, Flags), parsed_args(Command, Flags)) :- 37 | append(Command0, [Cmd], Command). 38 | 39 | options_upsert(Option, Options0, Options) :- 40 | functor(Option, Functor, Arity), 41 | functor(Pattern, Functor, Arity), 42 | ( append([Before, [Pattern], After], Options0) -> 43 | append([Before, [Option], After], Options) 44 | ; append(Options0, [Option], Options) 45 | ). 46 | 47 | chunked_args([]) --> []. 48 | chunked_args([CA|CAs]) --> chunked_arg(CA), chunked_args(CAs). 49 | 50 | chunked_arg(flag(help)) --> ( ["-h"] ; ["--help"] ). 51 | chunked_arg(flag(color(ColorPolicy))) --> 52 | ["--color"], 53 | [ColorPolicy0], 54 | { phrase(color_policy(ColorPolicy), ColorPolicy0) }. 55 | chunked_arg(flag(color(ColorPolicy))) --> 56 | [ColorPolicy0], 57 | { phrase(("--color=", color_policy(ColorPolicy)), ColorPolicy0) }. 58 | chunked_arg(command(install)) --> ["install"]. 59 | 60 | color_policy(auto) --> "auto". 61 | color_policy(always) --> "always". 62 | color_policy(never) --> "never". 63 | 64 | collect_relevant_env_vars(EnvVars) :- 65 | ( getenv("NO_COLOR", NC) -> 66 | NoColor = NC 67 | ; NoColor = unset 68 | ), 69 | ( getenv("CLICOLOR_FORCE", CCF) -> 70 | CliColorForce = CCF 71 | ; CliColorForce = unset 72 | ), 73 | EnvVars = [ 74 | no_color(NoColor), 75 | cli_color_force(CliColorForce) 76 | ]. 77 | 78 | resolve_flags_and_task(ParsedArgs, EnvVars, GlobalFlags, Task) :- 79 | ParsedArgs = parsed_args(Command, Flags), 80 | resolve_cli_color(EnvVars, Flags, CliColor), 81 | GlobalFlags = [ 82 | cli_color(CliColor) 83 | ], 84 | resolve_task(Command, Flags, Task). 85 | 86 | resolve_cli_color(EnvVars, Flags, CliColor) :- 87 | member(no_color(NoColor), EnvVars), 88 | member(cli_color_force(CliColorForce), EnvVars), 89 | ( member(color(ColorPolicy), Flags) -> 90 | true 91 | ; ColorPolicy = auto 92 | ), 93 | CliColor0 = on, 94 | ( NoColor \= unset, NoColor \= "" -> 95 | CliColor1 = off 96 | ; CliColor1 = CliColor0 97 | ), 98 | ( CliColorForce \= unset, CliColorForce \= "" -> 99 | CliColor2 = on 100 | ; CliColor2 = CliColor1 101 | ), 102 | ( ColorPolicy == always -> 103 | CliColor = on 104 | ; ColorPolicy == never -> 105 | CliColor = off 106 | ; CliColor = CliColor2 107 | ). 108 | 109 | resolve_task([], _, help(root)). 110 | resolve_task([Command|Subcommands], Flags, Task) :- 111 | resolve_command_task(Command, Subcommands, Flags, Task). 112 | 113 | resolve_command_task(install, [], Flags, Task) :- 114 | ( member(help, Flags) -> 115 | Task = help(install) 116 | ; Task = install 117 | ). 118 | 119 | set_global_state(GlobalFlags) :- 120 | ( member(cli_color(CliColor), GlobalFlags) -> 121 | assertz(cli_color(CliColor)) 122 | ; true 123 | ). 124 | 125 | do_task(help(CommandPath), _) :- 126 | phrase_to_stream(help_text(CommandPath), user_output). 127 | 128 | do_task(install, _) :- 129 | pkg_install(_). 130 | 131 | % Uses global color configuration 132 | ansi(Color) --> 133 | { 134 | cli_color(CliColor), 135 | phrase(ansi(CliColor, Color), Code) 136 | }, 137 | Code. 138 | 139 | ansi(off, _) --> "". 140 | ansi(on, Color) --> ansi_(Color). 141 | 142 | % Raw colors 143 | ansi_(reset) --> "\x1b\[0m". 144 | ansi_(bold) --> "\x1b\[1m". 145 | ansi_(red) --> "\x1b\[31m". 146 | ansi_(green) --> "\x1b\[32m". 147 | ansi_(yellow) --> "\x1b\[33m". 148 | ansi_(blue) --> "\x1b\[34m". 149 | ansi_(magenta) --> "\x1b\[35m". 150 | ansi_(cyan) --> "\x1b\[36m". 151 | ansi_(white) --> "\x1b\[37m". 152 | 153 | % Color aliases 154 | ansi_(help_header) --> ansi_(yellow), ansi_(bold). 155 | ansi_(help_option) --> ansi_(green), ansi_(bold). 156 | 157 | % Uses global color configuration 158 | color_text(Color, Text) --> 159 | { 160 | cli_color(CliColor), 161 | phrase(color_text(CliColor, Color, Text), ColorText) 162 | }, 163 | ColorText. 164 | 165 | % Accepts arbitrary grammar rule bodies 166 | color_text(CliColor, Color, Text) --> 167 | { phrase(Text, Text1) }, 168 | ansi(CliColor, Color), 169 | Text1, 170 | ansi(CliColor, reset). 171 | 172 | help_text(CommandPath) --> 173 | { 174 | help_info(CommandPath, Description, Usage, Options, Commands) 175 | }, 176 | help_description(Description), 177 | help_usage(Usage), 178 | help_option_table("Options", Options), 179 | help_option_table("Commands", Commands). 180 | 181 | help_info( 182 | root, 183 | "Bakage: an experimental package manager for Prolog", 184 | "bakage [OPTIONS] [COMMAND]", 185 | [ 186 | flag("color", none, "When to use color. Valid options: auto, always, never"), 187 | flag("help", "h", "Print help") 188 | ], 189 | [ 190 | %command("init", "Create a new project in the current directory"), 191 | command("install", "Installs the dependencies of the current package") 192 | %command("new", "Create a new project in a new directory"), 193 | %command("run", "Runs the current package") 194 | ] 195 | ). 196 | help_info( 197 | install, 198 | "Install package dependencies", 199 | "bakage install [OPTIONS]", 200 | [ 201 | flag("color", none, "When to use color. Valid options: auto, always, never"), 202 | flag("help", "h", "Print help") 203 | ], 204 | [] 205 | ). 206 | 207 | options_width(Options, OptionWidth) :- 208 | options_width(Options, 0, OptionWidth0), 209 | OptionWidth is 2 + OptionWidth0 + 2. 210 | 211 | options_width([], OptionWidth, OptionWidth). 212 | options_width([Option|Options], OptionWidth0, OptionWidth) :- 213 | option_width(Option, Width), 214 | OptionWidth1 is max(OptionWidth0, Width), 215 | options_width(Options, OptionWidth1, OptionWidth). 216 | 217 | option_width(flag(Long, _, _), Width) :- 218 | length(Long, Width0), 219 | Width is 4 + Width0. 220 | option_width(command(Name, _), Width) :- 221 | length(Name, Width). 222 | 223 | with_tail_newline([]) --> "\n". 224 | with_tail_newline([H|T]) --> 225 | { 226 | Text = [H|T], 227 | phrase((..., [Last]), Text), 228 | if_( 229 | Last = '\n', 230 | WithNewline = Text, 231 | phrase((Text, "\n"), WithNewline) 232 | ) 233 | }, 234 | WithNewline. 235 | 236 | help_description(Description) --> 237 | { phrase(Description, Description1) }, 238 | with_tail_newline(Description1). 239 | 240 | help_usage(Usage) --> 241 | "\n", 242 | color_text(help_header, "Usage:"), 243 | " ", 244 | color_text(help_option, Usage), 245 | "\n". 246 | 247 | help_option_table(_, []) --> "". 248 | help_option_table(Name, Options) --> 249 | { Options = [_|_] }, 250 | "\n", 251 | color_text(help_header, (Name, ":")), "\n", 252 | { options_width(Options, OptionWidth) }, 253 | help_option_table_(Options, OptionWidth). 254 | 255 | help_option_table_([], _) --> "". 256 | help_option_table_([Option|Options], OptionWidth) --> 257 | help_option_table_line(Option, OptionWidth), 258 | help_option_table_(Options, OptionWidth). 259 | 260 | n_spaces(N) --> 261 | { 262 | length(Spaces, N), 263 | append(Spaces, _, [' '|Spaces]) 264 | }, 265 | Spaces. 266 | 267 | help_option_table_line(command(Name, Description), OptionWidth) --> 268 | { 269 | length(Name, NameLen), 270 | PaddingLen is OptionWidth - NameLen - 2 271 | }, 272 | n_spaces(2), color_text(help_option, Name), n_spaces(PaddingLen), 273 | with_tail_newline(Description). 274 | 275 | help_option_table_line(flag(Long, Short, Description), OptionWidth) --> 276 | { 277 | length(Long, LongLen), 278 | PaddingLen is OptionWidth - LongLen - 6, 279 | if_( 280 | Short = none, 281 | phrase(n_spaces(4), ShortDesc), 282 | phrase( 283 | (color_text(help_option, ("-", Short)), ", "), 284 | ShortDesc 285 | ) 286 | ) 287 | }, 288 | n_spaces(2), 289 | ShortDesc, 290 | color_text(help_option, ("--", Long)), 291 | n_spaces(PaddingLen), 292 | with_tail_newline(Description). 293 | -------------------------------------------------------------------------------- /src/validation.pl: -------------------------------------------------------------------------------- 1 | :- use_module(library(lists)). 2 | :- use_module(library(reif)). 3 | :- use_module(library(debug)). 4 | :- use_module(library(files)). 5 | 6 | is_list_t(Ls, T) :- 7 | match(Ls, [ 8 | arm(ground([]), (T = true)), 9 | arm(composite([any | bind(Ls1)]), is_list_t(Ls1, T)), 10 | arm(any, (T = false)) 11 | ]). 12 | 13 | 14 | file_exists_t(P, true):- file_exists(P),!. 15 | file_exists_t(_, false). 16 | 17 | license_t_(license(name(N)), T):- is_list_t(N, T). 18 | 19 | license_t_(license(name(N), path(P)), T) :- 20 | call((is_list_t(N), is_list_t(P)), T). 21 | 22 | license_valid_path_t([], Result1, Result1). 23 | license_valid_path_t([_,_|_], Result1, Result1). 24 | license_valid_path_t([license(name(_))], Result1, Result1). 25 | license_valid_path_t([license(name(_), path(P))], Result1, Result2):- 26 | if_(file_exists_t(P), 27 | (Result2 = Result1), 28 | ( 29 | Err = "the path of the license is not valid", 30 | Result2 = error(Err), 31 | user_message_invalid_manifest(Err) 32 | ) 33 | ). 34 | 35 | license_t(X, T) :- 36 | match(X, [ 37 | arm(composite(license(bind(N))), (license_t_(license(N), T))), 38 | arm(composite(license(bind(N), bind(P))), (license_t_(license(N, P), T))), 39 | arm(any, (T = false)) 40 | ]). 41 | 42 | name_t(X, T) :- 43 | match(X, [ 44 | arm(composite(name(bind(N))), (is_list_t(N, T))), 45 | arm(any, (T = false)) 46 | ]). 47 | 48 | dependencies_t(X, T) :- 49 | match(X, [ 50 | arm(composite(dependencies(bind(N))), (is_list_t(N, T))), 51 | arm(any, (T = false)) 52 | ]). 53 | 54 | main_file_t(X, T) :- 55 | match(X, [ 56 | arm(composite(main_file(bind(N))), (is_list_t(N, T))), 57 | arm(any, (T = false)) 58 | ]). 59 | 60 | pattern_in_list([], _) --> []. 61 | 62 | pattern_in_list([L|Ls], Pattern_t) --> 63 | { 64 | if_(call(Pattern_t, L), 65 | Match = [L], 66 | Match = [] 67 | ) 68 | }, 69 | Match, 70 | pattern_in_list(Ls, Pattern_t). 71 | 72 | % A valid manifest 73 | valid_manifest_t(Manifest, Report, Valid) :- 74 | has_valid_name(Manifest, ValidName), 75 | has_valid_optional_main_file(Manifest, ValidMainFile), 76 | has_license(Manifest, ValidLicense), 77 | has_optional_dependencies(Manifest, ValidDendencies), 78 | if_((ValidName=success, ValidMainFile=success, ValidLicense=success, ValidDendencies=success), 79 | if_(memberd_t(dependencies(Deps), Manifest), 80 | ( 81 | phrase(valid_dependencies(Deps), DepsReport), 82 | if_(all_dependencies_valid_t(DepsReport), 83 | ( 84 | Report = [validate_manifest-success], 85 | Valid=true 86 | ), 87 | ( 88 | Report = DepsReport, 89 | Valid=false 90 | ) 91 | ) 92 | ), 93 | ( 94 | Report = [validate_manifest-success], 95 | Valid = true 96 | ) 97 | 98 | ), 99 | ( 100 | Report = [ 101 | validate_manifest_name-ValidName, 102 | validate_manifest_main_file-ValidMainFile, 103 | validate_manifest_license-ValidLicense, 104 | validate_dependencies-ValidDendencies 105 | ], 106 | Valid = false 107 | ) 108 | 109 | ). 110 | 111 | % the message sent to the user when a dependency is malformed 112 | user_message_malformed_dependency(D, Error):- 113 | current_output(Out), 114 | phrase_to_stream((portray_clause_(D), "is malformed: ", Error, "\n"), Out). 115 | 116 | % the message sent to the user when there is a validation error 117 | user_message_invalid_manifest(Error):- 118 | current_output(Out), 119 | phrase_to_stream(("The installation failed; cause:\n\t", Error, "\n"), Out). 120 | 121 | % Is valid when there is 0 instance and the field is optional 122 | has_a_field([], _, _, true, success). 123 | 124 | % Is not valid when there is 0 instance and the field is not optional 125 | has_a_field([], FieldName, PredicateForm, false, error(Es)):- 126 | phrase(format_("the '~s' of the package is not defined or does not have the a predicate of the form '~s'", [FieldName, PredicateForm]), Es), 127 | user_message_invalid_manifest(Es). 128 | 129 | % Is valid when there is one instance of the field and the field value has the correct type 130 | has_a_field([_], _, _, _, success). 131 | 132 | % Is not valid when there are multiple instances of the field 133 | has_a_field([_,_|_], FieldName, _, _, error(Es)):- 134 | phrase(format_("the package has multiple '~s'", [FieldName]), Es), 135 | user_message_invalid_manifest(Es). 136 | 137 | has_valid_name(Manifest, Result):- 138 | phrase(pattern_in_list(Manifest, name_t), S), 139 | has_a_field(S, "name", "name(N)", false, Result). 140 | 141 | has_valid_optional_main_file(Manifest, Result) :- 142 | phrase(pattern_in_list(Manifest, main_file_t), S), 143 | has_a_field(S, "main_file", "main_file(N)", true, Result). 144 | 145 | has_license(Manifest, Result) :- 146 | phrase(pattern_in_list(Manifest, license_t), S), 147 | has_a_field(S, "license", "license(name(N));license(name(N), path(P))", false, Result1), 148 | license_valid_path_t(S, Result1, Result). 149 | 150 | has_optional_dependencies(Manifest, Result):- 151 | phrase(pattern_in_list(Manifest, dependencies_t), S), 152 | has_a_field(S, "dependencies", "dependencies(D)", true, Result). 153 | 154 | % A valid dependency 155 | valid_dependencies([]) --> []. 156 | 157 | valid_dependencies([dependency(Name, path(Path))| Ds]) --> { 158 | if_( 159 | (memberd_t(';', Name) 160 | ; memberd_t('|', Name) 161 | ; memberd_t(';', Path) 162 | ; memberd_t('|', Path) 163 | ), 164 | ( 165 | Error = "the name and the path of the dependency should not contain an \";\" or an \"|\" caracter", 166 | M = validate_dependency(dependency(Name, path(Path)))-error(Error), 167 | user_message_malformed_dependency(dependency(Name, path(Path)), Error) 168 | ), 169 | M = validate_dependency(dependency(Name, path(Path)))-success 170 | ) 171 | }, 172 | [M], 173 | valid_dependencies(Ds). 174 | 175 | valid_dependencies([dependency(Name, git(Url))| Ds]) --> { 176 | if_( 177 | (memberd_t(';', Name) 178 | ; memberd_t('|', Name) 179 | ; memberd_t(';', Url) 180 | ; memberd_t('|', Url) 181 | ), 182 | ( 183 | Error = "the name of the dependency and the url should not contain an \";\" or an \"|\" caracter", 184 | M = validate_dependency(dependency(Name, git(Url)))-error(Error), 185 | user_message_malformed_dependency(dependency(Name, git(Url)), Error) 186 | ), 187 | M = validate_dependency(dependency(Name, git(Url)))-success 188 | ) 189 | }, 190 | [M], 191 | valid_dependencies(Ds). 192 | 193 | valid_dependencies([dependency(Name, git(Url, branch(Branch)))| Ds]) --> { 194 | if_( 195 | (memberd_t(';', Name) 196 | ; memberd_t('|', Name) 197 | ; memberd_t(';', Url) 198 | ; memberd_t('|', Url) 199 | ; memberd_t(';', Branch) 200 | ; memberd_t('|', Branch)), 201 | ( 202 | Error = "the name, the url and the branch of dependency should not contain an \";\" or an \"|\" caracter", 203 | M = validate_dependency(dependency(Name, git(Url, branch(Branch))))-error(Error), 204 | user_message_malformed_dependency(dependency(Name, git(Url, branch(Branch))), Error) 205 | ),( 206 | M = validate_dependency(dependency(Name, git(Url, branch(Branch))))-success 207 | ) 208 | ) 209 | }, 210 | [M], 211 | valid_dependencies(Ds). 212 | 213 | valid_dependencies([dependency(Name, git(Url, tag(Tag)))|Ds]) --> { 214 | if_( 215 | (memberd_t(';', Name) 216 | ; memberd_t('|', Name) 217 | ; memberd_t(';', Url) 218 | ; memberd_t('|', Url) 219 | ; memberd_t(';', Tag) 220 | ; memberd_t('|', Tag)), 221 | ( 222 | Error = "the name, the url and the tag of dependency should not contain an \";\" or an \"|\" caracter", 223 | M = validate_dependency(dependency(Name, git(Url, tag(Tag))))-error(Error), 224 | user_message_malformed_dependency(dependency(Name, git(Url, tag(Tag))), Error) 225 | ), 226 | M = validate_dependency(dependency(Name, git(Url, tag(Tag))))-success 227 | ) 228 | }, 229 | [M], 230 | valid_dependencies(Ds). 231 | 232 | valid_dependencies([dependency(Name, git(Url, hash(Hash)))|Ds]) --> { 233 | if_( 234 | (memberd_t(';', Name) 235 | ; memberd_t('|', Name) 236 | ; memberd_t(';', Url) 237 | ; memberd_t('|', Url) 238 | ; memberd_t(';', Hash) 239 | ; memberd_t('|', Hash)), 240 | ( 241 | Error = "the name, the url and the hash of dependency should not contain an \";\" or an \"|\" caracter", 242 | M = validate_dependency(dependency(Name, git(Url, hash(Hash))))-error(Error), 243 | user_message_malformed_dependency(dependency(Name, git(Url, hash(Hash))), Error) 244 | ), 245 | M = validate_dependency(dependency(Name, git(Url, hash(Hash))))-success 246 | ) 247 | }, 248 | [M], 249 | valid_dependencies(Ds). 250 | 251 | all_dependencies_valid_t([], true). 252 | all_dependencies_valid_t([validate_dependency(_)-success| Vs], T) :- all_dependencies_valid_t(Vs, T). 253 | all_dependencies_valid_t([validate_dependency(_)-error(_)| _], false). 254 | -------------------------------------------------------------------------------- /src/bakage.pl: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: Unlicense */ 2 | 3 | :- module(bakage, [pkg_install/1]). 4 | 5 | % ========================== 6 | % === Compatibility zone === 7 | % ========================== 8 | 9 | % Everything that is implementation specific, implementation defined, or otherwise not guaranteed 10 | % by the base 13211-1 ISO standard with corrigenda should be encapsulated here so that we only 11 | % have to look in one place when porting or making this more portable. 12 | 13 | % Hard to shim 14 | 15 | :- use_module(library(os), [argv/1, setenv/2, shell/1, unsetenv/1, getenv/2]). 16 | :- use_module(library(files), [ 17 | directory_exists/1, make_directory_path/1, directory_files/2, file_exists/1, 18 | delete_file/1, delete_directory/1, working_directory/2 19 | ]). 20 | :- use_module(library(dcgs), [phrase/2, phrase/3, ... //0]). % 13211-3 21 | :- use_module(library(charsio), [write_term_to_chars/3]). 22 | :- use_module(library(iso_ext), [setup_call_cleanup/3, call_cleanup/2]). 23 | 24 | % Easy to shim 25 | :- use_module(library(lists), [ 26 | length/2, maplist/1, member/2, append/2, maplist/2, append/3, memberchk/2 27 | ]). 28 | :- use_module(library(pio), [phrase_to_file/2, phrase_to_stream/2]). 29 | :- use_module(library(dif), [dif/2]). % With dif_si/2, though that may lose some functionality 30 | :- use_module(library(reif), [if_/3, (;)/3, memberd_t/3, (=)/3]). 31 | :- use_module(library(format), [portray_clause/1, portray_clause_//1]). 32 | 33 | user:term_expansion((:- use_module(pkg(Package))), (:- use_module(PackageMainFile))) :- 34 | package_main_file(Package, PackageMainFile). 35 | 36 | % ================================= 37 | % === End of compatibility zone === 38 | % ================================= 39 | 40 | 41 | % Cleanly pass arguments to a script through environment variables 42 | run_script_with_args(ScriptName, Args, Success) :- 43 | maplist(define_script_arg, Args), 44 | append(["sh scryer_libs/scripts/", ScriptName, ".sh"], Script), 45 | ( 46 | shell(Script) -> 47 | Success = true 48 | ; Success = false 49 | ), 50 | maplist(undefine_script_arg, Args). 51 | 52 | define_script_arg(Arg-Value) :- setenv(Arg, Value). 53 | undefine_script_arg(Arg-_) :- unsetenv(Arg). 54 | 55 | % join_sep_split/3: Monotonic join and split by separator. 56 | % Implementation by @bakaq from https://github.com/mthom/scryer-prolog/discussions/3121 57 | % 58 | % True when `Joined` is a list consisting of all the lists in `Segments` 59 | % separated by `Separator`, and `Separator` doesn't appear in any of the 60 | % lists in `Segments`. Can be used as both a split and a join depending 61 | % on the mode. 62 | join_sep_split(Joined, Separator, Segments) :- 63 | join_sep_split_(Separator, Joined, Segments). 64 | 65 | % The empty separator case is just append/2 66 | join_sep_split_([], Joined, Segments) :- 67 | append(Segments, Joined). 68 | join_sep_split_([S|Ss], Joined, Segments) :- 69 | join_sep_split(Joined, [], [S|Ss], Segments, []). 70 | 71 | % Stuff like this that needs 2 list differences is hard to encode 72 | % in a DCG in my experience. 73 | join_sep_split([], [], _, [], []). 74 | join_sep_split([L0|LT0], Ls, Sep, [Seg|Segs0], Segs) :- 75 | Ls0 = [L0|LT0], 76 | next_segment(Ls0, Seg, Sep, Ls1, Reason), 77 | join_sep_split_(Ls1, Ls, Sep, Segs0, Segs, Reason). 78 | 79 | join_sep_split_([], [], _, Segs0, Segs, Reason) :- 80 | reason_segs(Reason, Segs0, Segs). 81 | join_sep_split_([L0|LT0], Ls, Sep, Segs0, Segs, _) :- 82 | join_sep_split([L0|LT0], Ls, Sep, Segs0, Segs). 83 | 84 | reason_segs(sep, [[]|Segs], Segs). 85 | reason_segs(end, Segs, Segs). 86 | 87 | next_segment([], [], _, [], end). 88 | next_segment([L0|Ls0], Seg, Sep, Ls, Reason) :- 89 | if_( 90 | starts_with_t(Sep, [L0|Ls0], Ls1), 91 | (Ls = Ls1, Seg = [], Reason = sep), 92 | (Seg = [L0|Seg1], next_segment(Ls0, Seg1, Sep, Ls, Reason)) 93 | ). 94 | 95 | starts_with_t([], Ls, Ls, true). 96 | starts_with_t([S|Ss], Ls0, Ls, T) :- 97 | starts_with_t_(Ls0, Ls, S, Ss, T). 98 | 99 | starts_with_t_([], _, _, _, false). 100 | starts_with_t_([L|Ls0], Ls, S, Ss, T) :- 101 | if_( 102 | S = L, 103 | starts_with_t(Ss, Ls0, Ls, T), 104 | T = false 105 | ). 106 | 107 | find_project_root(Root) :- 108 | working_directory(CWD, CWD), 109 | find_project_root_from(CWD, Root). 110 | 111 | find_project_root_from(Dir, Root) :- 112 | append(Dir, "/scryer-manifest.pl", ManifestPath), 113 | ( file_exists(ManifestPath) -> 114 | Root = Dir 115 | ; join_sep_split(Dir, "/", Segments), 116 | append(ParentSegments, [_LastSegment], Segments), 117 | ParentSegments \= [], 118 | join_sep_split(ParentDir, "/", ParentSegments), 119 | find_project_root_from(ParentDir, Root) 120 | ). 121 | 122 | scryer_path(ScryerPath) :- 123 | ( getenv("SCRYER_PATH", EnvPath) -> 124 | ScryerPath = EnvPath 125 | ; find_project_root(RootChars), 126 | append([RootChars, "/scryer_libs"], ScryerPath) 127 | ). 128 | 129 | 130 | % A prolog file knowledge base represented as a list of terms 131 | prolog_kb_list(Stream) --> {read(Stream, Term), dif(Term, end_of_file)}, [Term], prolog_kb_list(Stream). 132 | prolog_kb_list(Stream) --> {read(Stream, Term), Term == end_of_file}, []. 133 | 134 | parse_manifest(Filename, Manifest) :- 135 | setup_call_cleanup( 136 | open(Filename, read, Stream), 137 | catch(once(phrase(prolog_kb_list(Stream), Manifest)), error(E, I), throw(error_formating_of_manifest(E, I))), 138 | close(Stream) 139 | ). 140 | 141 | package_main_file(Package, PackageMainFile) :- 142 | atom_chars(Package, PackageChars), 143 | scryer_path(ScryerPath), 144 | append([ScryerPath, "/packages/", PackageChars], PackagePath), 145 | append([PackagePath, "/", "scryer-manifest.pl"], ManifestPath), 146 | parse_manifest(ManifestPath, Manifest), 147 | member(main_file(MainFile), Manifest), 148 | append([PackagePath, "/", MainFile], PackageMainFileChars), 149 | atom_chars(PackageMainFile, PackageMainFileChars). 150 | 151 | % This creates the directory structure we want 152 | ensure_scryer_libs :- 153 | ( directory_exists("scryer_libs") -> 154 | true 155 | ; make_directory_path("scryer_libs") 156 | ), 157 | ( directory_exists("scryer_libs/packages") -> 158 | true 159 | ; make_directory_path("scryer_libs/packages") 160 | ), 161 | ( directory_exists("scryer_libs/scripts") -> 162 | true 163 | ; make_directory_path("scryer_libs/scripts"), 164 | ensure_scripts 165 | ), 166 | ( directory_exists("scryer_libs/temp") -> 167 | true 168 | ; make_directory_path("scryer_libs/temp") 169 | ). 170 | 171 | % Installs helper scripts 172 | ensure_scripts :- 173 | findall(ScriptName-ScriptString, script_string(ScriptName, ScriptString), Scripts), 174 | maplist(ensure_script, Scripts). 175 | 176 | ensure_script(Name-String) :- 177 | append(["scryer_libs/scripts/", Name, ".sh"], Path), 178 | phrase_to_file(String, Path). 179 | 180 | 181 | % Predicate to install the dependencies 182 | pkg_install(Report) :- 183 | parse_manifest("scryer-manifest.pl", Manifest), 184 | ensure_scryer_libs, 185 | setenv("SHELL", "/bin/sh"), 186 | setenv("GIT_ADVICE", "0"), 187 | directory_files("scryer_libs/packages", Installed_Packages), 188 | if_(valid_manifest_t(Manifest, Validation_Report), 189 | (member(dependencies(Deps), Manifest) -> 190 | call_cleanup( 191 | ( 192 | logical_plan(Plan, Deps, Installed_Packages), 193 | installation_execution(Plan, Installation_Report), 194 | append(Validation_Report, Installation_Report, Report) 195 | ), 196 | delete_directory("scryer_libs/temp") 197 | ) ; Report = Validation_Report 198 | ), 199 | Report = Validation_Report 200 | ). 201 | 202 | % A logical plan to install the dependencies 203 | logical_plan(Plan, Ds, Installed_Packages) :- 204 | phrase(fetch_plan(Ds, Installed_Packages), Plan). 205 | 206 | % A logical plan to fetch the dependencies 207 | fetch_plan([], _) --> []. 208 | fetch_plan([D|Ds], Installed_Packages) --> 209 | {fetch_step(D, Installation_Step, Installed_Packages)}, 210 | [Installation_Step], 211 | fetch_plan(Ds, Installed_Packages). 212 | 213 | 214 | % A step of a logical plan to fetch the dependencies 215 | fetch_step(dependency(Name, DependencyTerm), Step, Installed_Packages) :- 216 | if_(memberd_t(Name, Installed_Packages), 217 | Step = do_nothing(dependency(Name, DependencyTerm)), 218 | Step = install_dependency(dependency(Name, DependencyTerm)) 219 | ). 220 | 221 | % Execute the physical installation of the dependencies 222 | installation_execution(Plan, Results):- 223 | ensure_dependencies(Plan, Success), 224 | if_(Success = false, 225 | phrase(fail_installation(Plan), Results), 226 | true 227 | ), 228 | parse_install_report(Result_Report), 229 | phrase(installation_report(Plan, Result_Report), Results). 230 | 231 | % All dependency installation failed 232 | fail_installation([]) --> []. 233 | fail_installation([P|Ps]) --> [P-error("installation script failed")], fail_installation(Ps). 234 | 235 | 236 | % Parse the report of the installation of the dependencies 237 | parse_install_report(Result_List) :- 238 | setup_call_cleanup( 239 | open("scryer_libs/temp/install_resp.pl", read, Stream), 240 | once(phrase(prolog_kb_list(Stream), Result_List)), 241 | ( 242 | close(Stream), 243 | ( file_exists("scryer_libs/temp/install_resp.pl")-> 244 | delete_file("scryer_libs/temp/install_resp.pl") 245 | ; true 246 | ) 247 | ) 248 | ). 249 | 250 | % The installation report of the dependencies 251 | installation_report([], _) --> []. 252 | installation_report([P|Ps], Result_Report) --> 253 | { report_installation_step(P, Result_Report, R) }, 254 | [R], 255 | installation_report(Ps, Result_Report). 256 | 257 | % The result of a logical step 258 | report_installation_step(do_nothing(dependency(Name, DependencyTerm)), _, do_nothing(dependency(Name, DependencyTerm))-success). 259 | 260 | 261 | report_installation_step(install_dependency(dependency(Name, DependencyTerm)), ResultMessages, install_dependency(dependency(Name, DependencyTerm))-Message):- 262 | memberchk(result(Name, Message), ResultMessages). 263 | 264 | % Execute the logical plan 265 | ensure_dependencies(Logical_Plan, Success) :- 266 | phrase(physical_plan(Logical_Plan), Physical_Plan), 267 | Args = [ 268 | "DEPENDENCIES_STRING"-Physical_Plan 269 | ], 270 | run_script_with_args("ensure_dependencies", Args, Success). 271 | 272 | 273 | % Create a physical plan in shell script 274 | physical_plan([]) --> []. 275 | physical_plan([P|Ps]) --> physical_plan_([P|Ps]). 276 | 277 | physical_plan_([P]) --> { 278 | physical_plan_step(P, El) 279 | }, 280 | El. 281 | 282 | physical_plan_([P|Ps]) --> { 283 | physical_plan_step(P, El) 284 | }, 285 | El, 286 | "|", 287 | physical_plan_(Ps). 288 | 289 | % Create a step for the shell script physical plan 290 | physical_plan_step(do_nothing(dependency(Name, D)) , El) :- 291 | write_term_to_chars(D, [quoted(true), double_quotes(true)], DependencyTermChars), 292 | append(["dependency_term=", DependencyTermChars, ";dependency_name=", Name, ";dependency_kind=do_nothing"], El). 293 | 294 | physical_plan_step(install_dependency(dependency(Name, git(Url))) ,El):- 295 | write_term_to_chars(git(Url), [quoted(true), double_quotes(true)], DependencyTermChars), 296 | append(["dependency_term=", DependencyTermChars, ";dependency_name=", Name, ";dependency_kind=git_default;git_url=", Url], El). 297 | 298 | physical_plan_step(install_dependency(dependency(Name, git(Url,branch(Branch)))) ,El):- 299 | write_term_to_chars(git(Url,branch(Branch)), [quoted(true), double_quotes(true)], DependencyTermChars), 300 | append(["dependency_term=", DependencyTermChars, ";dependency_name=", Name, ";dependency_kind=git_branch;git_url=", Url, ";git_branch=", Branch], El). 301 | 302 | physical_plan_step(install_dependency(dependency(Name, git(Url,tag(Tag)))) ,El):- 303 | write_term_to_chars(git(Url,tag(Tag)), [quoted(true), double_quotes(true)], DependencyTermChars), 304 | append(["dependency_term=", DependencyTermChars, ";dependency_name=", Name, ";dependency_kind=git_tag;git_url=", Url, ";git_tag=", Tag], El). 305 | 306 | physical_plan_step(install_dependency(dependency(Name, git(Url,hash(Hash)))) ,El):- 307 | write_term_to_chars(git(Url,hash(Hash)), [quoted(true), double_quotes(true)], DependencyTermChars), 308 | append(["dependency_term=", DependencyTermChars, ";dependency_name=", Name, ";dependency_kind=git_hash;git_url=", Url, ";git_hash=", Hash], El). 309 | 310 | physical_plan_step(install_dependency(dependency(Name, path(Path))) ,El):- 311 | write_term_to_chars(path(Path), [quoted(true), double_quotes(true)], DependencyTermChars), 312 | append(["dependency_term=", DependencyTermChars, ";dependency_name=", Name, ";dependency_kind=path;dependency_path=", Path], El). 313 | --------------------------------------------------------------------------------