{"id":26119358,"url":"https://github.com/baldomo/makesh","last_synced_at":"2025-04-13T10:41:29.386Z","repository":{"id":110086792,"uuid":"455664376","full_name":"Baldomo/makesh","owner":"Baldomo","description":"Makefile-sorta-kinda-alike Bash task runner","archived":false,"fork":false,"pushed_at":"2025-01-23T09:01:34.000Z","size":104,"stargazers_count":5,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-03-27T02:06:59.522Z","etag":null,"topics":["bash","build-tool","task-runner"],"latest_commit_sha":null,"homepage":"","language":"Shell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"agpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Baldomo.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-02-04T19:02:29.000Z","updated_at":"2025-02-07T20:00:48.000Z","dependencies_parsed_at":"2025-01-23T10:18:57.287Z","dependency_job_id":"0d5c3f23-c340-4b78-a327-f6c59712f9fb","html_url":"https://github.com/Baldomo/makesh","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Baldomo%2Fmakesh","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Baldomo%2Fmakesh/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Baldomo%2Fmakesh/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Baldomo%2Fmakesh/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Baldomo","download_url":"https://codeload.github.com/Baldomo/makesh/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248701387,"owners_count":21148024,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bash","build-tool","task-runner"],"created_at":"2025-03-10T12:34:15.318Z","updated_at":"2025-04-13T10:41:29.361Z","avatar_url":"https://github.com/Baldomo.png","language":"Shell","funding_links":[],"categories":[],"sub_categories":[],"readme":"# makesh \u003c!-- omit in toc --\u003e\n`makesh` is a simple requirement-based task runner similar to GNU Make, minus the C-oriented build system. `makesh` is also written in Bash.\n\nThis project was born of necessity and many late hours wasted on writing files for Make and other such task runners, but it's not meant as a complete replacement. Compatibility with Bash versions older than 5.0 is not guaranteed.\n\n### Table of contents\n- [Installation](#installation)\n- [Usage](#usage)\n  - [Writing targets](#writing-targets)\n  - [CLI](#cli)\n- [Library](#library)\n  - [`generate.sh`](#generatesh)\n  - [`lib.sh`](#libsh)\n  - [`runtime.sh`](#runtimesh)\n  - [`message.sh`](#messagesh)\n  - [`parseopts.sh`](#parseoptssh)\n  - [`util.sh`](#utilsh)\n\n## Installation\n`makesh` is built to be used as a [Git submodule](https://git-scm.com/book/en/v2/Git-Tools-Submodules) for easy update and usage inside a bigger project/repository.\n\n```shell\n$ git submodule add https://github.com/Baldomo/makesh.git\n$ git submodule update --init\n```\n\nYou can update `makesh` to the latest version with\n\n```shell\n$ ./make --update\n```\n\nwhich basically runs \n\n```shell\n$ git submodule update --remote \"$makesh_lib_dir\"\n```\n\n## Usage\nTo start using `makesh` after placing the submodule in your project directory, just run\n\n```shell\n$ makesh/generate.sh\n```\n\n\u003e [!NOTE]\n\u003e See `makesh/generate.sh --help` for more information and CLI options.\n\nThis will create a simple `make` shell script in your current directory (using `pwd`) with the basic imports and a default target.\nYou will only need to write your build targets as explained in the rest of the documentation, `makesh` will take care of the CLI and utilities.\n\nA `.shellcheckrc` will also be generated alongside the script with useful defaults for Shellcheck users (mainly to disable [SC2317](https://github.com/koalaman/shellcheck/wiki/SC2317)). This is the default behaviour but it can be disabled using the corresponding CLI flag.\n\nYou can run a target by calling\n\n```shell\n$ ./make \u003ctarget\u003e\n```\n\nor, without specifying a target, `make::all` will be called\n\n```shell\n$ ./make\n```\n\n### Writing targets\nTargets are to be defined before importing `runtime.sh`. Each \"target\" is a Bash function which:\n1. has a name starting with `make::`\n2. can produces files and skip running if files are already present\n3. can skip running if arbitrary files have not been change since the last run (kind of like Make, see [`lib::needs_changes()`](#libneeds_changes))\n4. can ignore present files and run anyway (\"force\", see [`$makesh_force`](#makesh_force))\n5. can call other targets before (or after) doing whatever it needs to do\n6. can return and be skipped arbitrarily (see API)\n7. can use utility functions provided by `makesh`\n8. provides its own documentation with special comment syntax\n\n\u003e [!TIP]\n\u003e Think of your `makesh` script more like a normal Bash script than a special file: being simple functions, targets are really powerful! You can source run any other function or command, create and remove files and even source other `makesh` scripts (see below).\n\nFor example:\n\n```sh\n#:(your_target) First line of documentation for your_target\n#:(your_target) Second line of documentation  (8)\nmake::your_target() {                       # (1)\n    lib::needs_change \"main.c\"              # (3)\n    lib::check_file \"main.o\"                # (2) and (4)\n    lib::requires \"other_target\"            # (5)\n    [[ -f \"yourfile\" ]] \u0026\u0026 lib::return      # (6)\n    # Using \"lib::\" functions implies (7)\n}\n```\n\n\u003e Keep in mind that the `make::all` target can be executed by not passing any target name to the `make` script, as mentioned above.\n\nYou can also just source other files containing valid targets (and **just** the targets, no sourcing of `makesh` files) in your `make` script and use them just the same as if they were in the main script. Note that `source` will not change the current working directory.\n\n```sh\n# scripts/utility.sh\n\nmake::utility() {\n    msg::msg \"Included target\"\n}\n```\n\n```sh\n# make\n\nmake::all() {\n    source ./include.sh\n    lib::requires make::utility\n    msg::msg \"Hello world!\"\n}\n```\n\nRunning `./make` will output:\n```\n==\u003e Running target make::all\n==\u003e Included target\n==\u003e Hello world!\n```\n\n### CLI\nThe CLI is provided automatically by sourcing `runtime.sh`. It can:\n- run targets\n- set the \"force\" with which to call a target (see [`$makesh_force`](#makesh_force))\n- show documentation for a target\n- show documentation and usage information for itself\n- check if a called target exists or not\n\nFor more information and full usage, see\n```shell\n$ ./make --help\n```\n\n## Library\n\n\u003e [!WARNING]\n\u003e The API is very prone to breaking changes, but the documentation will always be fairly extensive.\n\n`makesh` provides a simple utility library in the form of `source`-able shell scripts. Generally, sourcing one of such files will add:\n- functions prefixed by a sort of namespace like `lib::` or `msg::`\n- variables prefixed by `makesh_`.\n\nThe \"standard library\" structure can be summed up as follows:\n\n\u003ctable\u003e\n  \u003ctbody\u003e\n    \u003ctr\u003e\n      \u003cth\u003eFile\u003c/th\u003e\n      \u003cth\u003eFunctions\u003c/th\u003e\n      \u003cth\u003eVariables\u003c/th\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003ctd\u003e\u003ca href=\"#libsh\"\u003e\u003ccode\u003elib.sh\u003c/code\u003e\u003c/a\u003e\u003c/td\u003e\n      \u003ctd\u003e\u003ccode\u003elib::*\u003c/code\u003e\u003c/td\u003e\n      \u003ctd\u003e\u003ccode\u003e$makesh_*\u003c/code\u003e\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003ctd\u003e\u003ca href=\"#messagesh\"\u003e\u003ccode\u003emessage.sh\u003c/code\u003e\u003c/a\u003e\u003c/td\u003e\n      \u003ctd\u003e\u003ccode\u003emsg::*\u003c/code\u003e\u003c/td\u003e\n      \u003ctd\u003e\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003ctd\u003e\u003ca href=\"#parseoptssh\"\u003e\u003ccode\u003eparseopts.sh\u003c/code\u003e\u003c/a\u003e\u003c/td\u003e\n      \u003ctd\u003e\u003ccode\u003elib::parseopts\u003c/code\u003e\u003c/td\u003e\n      \u003ctd\u003e\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003ctd\u003e\u003ca href=\"#utilsh\"\u003e\u003ccode\u003eutils.sh\u003c/code\u003e\u003c/a\u003e\u003c/td\u003e\n      \u003ctd\u003e\u003ccode\u003eutil::*\u003c/code\u003e\u003c/td\u003e\n      \u003ctd\u003e\u003c/td\u003e\n    \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\n---\n\n### `generate.sh`\nSimple shell script which generates a `make` example file in your root project directory (or an arbitrary directory).\nGet more information with\n\n```shell\n$ makesh/generate.sh --help\n```\n\n---\n\n### `lib.sh`\nContains useful functions and variables to be used when writing targets. \nThe code is fairly well-documented, reading it directly is recommended.\n\n#### `$makesh_force`\nCounts how many --force were used (as an integer number). Gets decreased by 1 each time `lib::requires` is called. You can use it explicitly in your targets when you want to be able to skip running a command if not forced, for example:\n\n```sh\nmake::your_target() {\n    if (( ! makesh_force )); then\n        # This will run if makesh_force is zero\n        lib::return \"I don't need to do stuff\"\n    fi\n\n    # This will only run if makesh_force is higher than zero\n    do_stuff\n}\n```\n\nNote that functions under `lib::` already check `makesh_force` and act accordingly, see the implementation of `lib::check_file`:\n\n```sh\nlib::check_file() {\n    # Returns from the caller target if file exists \n    # AND makesh_force is zero\n    if [ -f \"$(realpath \"$1\")\" ] \u0026\u0026 (( ! makesh_force )); then\n        lib::return \"file $1 already exists\"\n    fi\n}\n```\n\n#### `$makesh_script`\nThe absolute path to the root `make` script in the project directory. For example `/home/user/project/make`.\n\n#### `$makesh_script_dir`\nThe absolute path of the directory of `$makesh_script` (the project directory). For example `/home/user/project`.\n\n#### `$makesh_lib_dir`\nThe absolute path of the directory containing the `makesh` library. For example `/home/user/project/makesh`.\n\n#### `$makesh_cache_dir`\nThe absolute path of the file cache directory. Defaults to `$makesh_lib_dir/cache`.\n\n#### `$makesh_enable_cache`\nIf set to 0, disables the file change cache. If the cache is disabled, `lib::needs_change` will never skip the target which called it.\n\n#### `$makesh_enable_cache_autoclean`\nDisables clearing the cache automatically when calling the `make::clean` target if set to 0 (zero).\n\n#### `lib::needs_changes()`\nChecks changes for a set of files by comparing the last modified time of the file to an empty file inside `$makesh_cache_dir`. If the file in the project directory is older than the cache, the target is not executed (basically works like Make checking changes in files). Also follows links if the given files are symlinks.\n\n```make\ntarget: main.sh other.sh\n\t@echo \"Target was run\"\n```\n\nand\n\n```sh\nmake::target() {\n    lib::needs_changes main.sh other.sh\n    echo \"Target was run\"\n}\n```\n\nare equivalent.\n\n#### `lib::clean_cache()`\nDeletes the file cache directory, but only if it is somewhere inside the project directory. Will be ran automatically when calling the `make::clean` target, even if such a target does not exist.\n\nUsage examples:\n```sh\nlib::clean_cache\n```\n\n#### `lib::check_dir()`\nBreak from current target if directory `$1` exists. Does not support wildcards. Can accept relative paths.\n\nUsage examples:\n```sh\nlib::check_dir \"some_dir\"\n```\n\n#### `lib::check_file()`\nBreak from current target if file `$1` exists. Does not support wildcards. Can accept relative paths.\n\nUsage examples:\n```sh\nlib::check_file \"some_dir/some_file\"\n```\n\n#### `lib::requires()`\nRun another target before the caller, passing `$makesh_force` decreased by 1.\nThis lets you have granular control over the depth to which propagate `--force` to the called targets. Will also forward all extra arguments to the required target.\n\nUsage examples:\n```sh\nlib::requires \"other_target\"\n\nlib::requires other_target \"string argument\"\n\nlib::requires make::other_target\n```\n\n#### `lib::return()`\nExits from the current target (and *only* the current target) unconditionally. Can be used to exit on arbitrary rules, for example:\n\n```sh\nif [[ \"test\" = \"test\" ]]; then\n    lib::return \"optional message\"\nfi\n```\n\nWill basically resume execution after skipping a single target, in short.\n\n---\n\n### `runtime.sh`\nContains code for the main entrypoint of the generated `make` script. Does not export functions. Generates output for `--help` automatically and parses command line flags.\n\n---\n\n### `message.sh`\nA modified version of `/usr/share/makepkg/util/message.sh` from Arch's `makepkg` packaging software.\n\nContains function for pretty formatted output. All functions are `printf`-like (first parameter is a string with formatting instructions, all other parameters are printed as per the instructions), for example:\n\n```sh\nmsg::msg \"Simple single message\"\n\nmsg::error \"Docker container failed to start: %s\" \"$container_name\"\n\nmsg::die \"Critical error during execution\"\n```\n\n#### `msg::ask()`\nUseful for asking user input, will not print a newline (`\\n`) after the text. \nOutput format:\n```\n:: Your text?\n```\n\n#### `msg::die()`\nCalls `msg::error` then `exit 1`, so works the same as that function (see below)\n\n#### `msg::error()`\nUsed to print errors, prints to `stderr`\nOutput format:\n```\n==\u003e ERROR: (\u003ctarget name\u003e) Your text\n```\n\n#### `msg::msg()`\nUsed to print generic messages.\nOutput format:\n```\n==\u003e Your text\n```\n\n#### `msg::msg2()`\nUsed to print less important generic messages or second-level messages.\nOutput format:\n```\n  -\u003e Your text\n```\n\n#### `msg::plain()`\nPrimarily used to continue a previous message on a new line.\nOutput format:\n```\n    Your text\n```\n\n#### `msg::plainerror()`\nPrimarily used to continue a previous error message on a new line, prints to `stderr`.\nOutput format:\n```\n    Your text\n```\n\n#### `msg::warning()`\nUsed to print warnings, prints to `stderr`\nOutput format:\n```\n==\u003e WARNING: Your text\n```\n\n#### `msg::colorize()`\nActivates output colorization if supported by the output terminal. Used internally.\n\n---\n\n### `parseopts.sh`\nA modified version of `/usr/share/makepkg/util/parseopts.sh` from Arch's `makepkg` packaging software.\nContains a single function: `lib::parseopts`, see [the source code](/parseopts.sh) for actual documentation.\n\n\u003e For both short and long flags\n\u003e - options requiring an argument should be suffixed with a colon (`:`)\n\u003e - options with optional arguments should be suffixed with a question mark (`?`).\n\nExample usage from `runtime.sh`:\n```sh\nOPT_SHORT=\"fh?u\"\nOPT_LONG=(\"force\" \"help?\" \"update\")\nif ! lib::parseopts \"$OPT_SHORT\" \"${OPT_LONG[@]}\" -- \"$@\"; then\n    msg::error \"Error parsing command line.\"\n    _usage\n    exit 1\nfi\nset -- \"${OPTRET[@]}\"\nunset OPT_SHORT OPT_LONG OPTRET\n\ndeclare makesh_help makesh_update\nwhile true; do\n    case \"$1\" in\n        -f|--force)  (( makesh_force++ )) ;;\n        -h|--help)   makesh_help=1 ;;\n        -u|--update) makesh_update=1 ;;\n        --)          shift; break 2 ;;\n    esac\n    shift\ndone\n\n# All remaining arguments are left in \"$@\"\n```\n\n---\n\n### `util.sh`\nMiscellaneous utilities, of which public functions are under `util::`. Documentation can be found in the code. Contains:\n- An adaptation of [mkropat/sh-realpath](https://github.com/mkropat/sh-realpath), a portable, Bash-ish implementation of `realpath`. Functions **do not** accept options like the real program. Originally MIT licensed.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbaldomo%2Fmakesh","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbaldomo%2Fmakesh","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbaldomo%2Fmakesh/lists"}