{ "info": { "author": "Ames Cornish", "author_email": "goodmake@montebellopartners.com", "bugtrack_url": null, "classifiers": [], "description": "What's GoodMake?\n================\n\nGoodMake is a simpler and more flexible build system. It lets you use a\nscripting language of your choice to define recipes and dependencies,\nand then intelligently runs just the right recipes, in parallel, to\nbuild the result you want. You do *not* have to know ahead of time what\nthe dependencies will be. This greatly simplifies writing build recipes\n-- you just specify target patterns and don't have to learn a\nspecial-purpose build system language.\n\nGoodMake's design borrows heavily from the grand-daddy of make systems,\n`Gnu Make `__, and from the\nwonderful `Redo `__ system.\n\nCompared to Gnu Make\n====================\n\nGoodMake is designed to allow much more powerful descriptions of the\ndependencies between recipes, while being simple to read, write, and\ndebug, resulting in more reliable build systems. GoodMake:\n\n- Lets you use any scripting language for your recipes\n- Doesn't require you to learn a make file \"language\"\n- Lets you write arbitrary code to determine dependencies\n- Lets you organize your recipes into separate make files\n- Plays well with recursive make files\n- Uses file checksums to determine if dependencies have changed\n- You can run your make files from any directory\n\nCompared to Redo\n================\n\nGoodMake:\n\n- Lets you use any scripting language for your recipes\n- Lets you consolidate multiple recipes into a single file\n- Lets you specify multiple target patterns that use the same recipe\n- Does not require your make files to be in any particular place\n\nUsage\n=====\n\nHere's a moderately sophisticated and heavily-commented shell-script\nexecutable \"make.sh\" file, which contains 5 recipes:\n\n::\n\n #! /usr/local/bin/goodmake /bin/sh -sex\n\n # Default action is to sort all files in src directory\n # \"!default\" indicates that \"default\" is a dummy target. Don't checksum it.\n #? !default\n $0 all\n\n # The \"*\" matches all targets, so this variable is always set\n #? *\n SRC_DIR=src\n\n # Make a target file by sorting a source file\n #? tgt/*\n # $0 - This goodmake script\n # $1 - The target, which matches one of the #? patterns for this recipe\n\n # Get the name of the corresponding source file\n SRC=$SRC_DIR/$(basename $1)\n\n # Flag the source file as a dependency\n $0 $SRC\n\n # Sort the source into the target\n sort $SRC > $1\n\n # Sort all files in src directory\n #? !all\n # The tgt list itself is a dependency\n $0 tgt.ls\n # Make all the listed target files\n $0 $(cat tgt.ls)\n\n # Make a list of all target files, based on source files\n # \"#!\" means to always run this recipe, in case files in $SRC_DIR changed\n #! tgt.ls\n ls $SRC_DIR | while read file; do\n echo tgt/$file\n done > $1\n\nTo sort all the files, you'd simply type:\n\n::\n\n ./make.sh\n\nThis will sort all the files in the directory ``src``, and put the\nsorted results in ``tgt``. The ``$0`` variable is the make script\n``./make.sh``, and the ``$1`` variable is the target currently being\nbuilt. The build works as follows:\n\n1. The ``./make.sh`` script with no arguments runs the recipe for\n ``default``\n2. The ``default`` recipe requires ``all``, which requires ``tgt.ls``,\n which lists the files in ``src`` into a file called ``tgt.ls``. If\n new files are added to ``src``, the ``tgt.ls`` will change, causing\n the ``all`` recipe to re-run.\n3. Next, the ``all`` recipe requires corresponding ``tgt/filename``\n dependencies for each ``src/filename`` from the ``tgt.ls``. If any of\n these ``src`` files have changed, the corresponding ``tgt/*`` pattern\n recipe will be re-run.\n4. A ``tgt/filename`` requirement uses the ``tgt/*`` recipe to create\n the target by running ``sort`` on the corresponding ``src`` file.\n\nIf you want to create or update just one sorted file, you could type:\n\n::\n\n ./make.sh tgt/filename\n\nGoodmake Script File Syntax\n===========================\n\nA GoodMake script file is a file in a scripting language of your choice,\nalong with strategically placed comments that break the file up into\n\"recipes\" to be run depending on the build target. You'll typically want\nto make the GoodMake file executable.\n\nThe familiar first comment line is the OS \"shebang\" that says this\nshould be executed with GoodMake. The line points to goodmake, and\nspecifies an interpreter command. Here is a Python shebang:\n\n::\n\n #! /usr/local/bin/goodmake /usr/bin/python3 -\n\nand a Bash shebang:\n\n::\n\n #! /usr/local/bin/goodmake /bin/sh -se\n\nSubsequent blank and comment lines are ignored. \"Stanzas\" of code are\nintroduced with a \"sheque\" comment that specifies (shell glob) targets\nthat the stanza will apply to. For example a Python or Bash script might\ninclude the target line:\n\n::\n\n #? tgt/sorted.txt\n\nAnd a node script might include the target line:\n\n::\n\n //? tgt/sorted.txt\n\nA \"shebang\" anywhere other that the first line, is like a \"sheque\" but\nindicates the recipe should always be run, regardless of any\ndependencies:\n\n::\n\n #! tgt/sorted.txt\n\nAfter a shebang, lines are interpreted. A recipe for a target is built\nout of *all* of the matching stanzas. In the recipe, the positional\narguments are set:\n\n0. The script path (depending on the interpreter)\n1. The target\n2. The script path (regardless of interpreter)\n\nHere's a full python ``make.py`` script file example:\n\n::\n\n #! /usr/local/bin/goodmake /usr/bin/python3 -\n \n #? tgt/*\n import subprocess\n import sys\n\n #? tgt/sorted.txt\n inputFile = 'src/input.txt'\n subprocess.run([sys.argv[2], inputFile])\n\n with open(inputFile) as input:\n lines = input.readlines()\n lines.sort()\n with open(sys.argv[1], 'w') as output:\n output.writelines(lines)\n\n #! !sayhi\n print(\"Hello, World\")\n\nWhen Recipes Are Run\n====================\n\nWhen an recipe is run, it may update the target, it creates a checksum,\nand it logs a build. A recipe is run when one of its target patterns is\nrequested, and:\n\n- The recipe starts with shebang (#!), or\n- There's no successful build log, or\n- The checksum has changed, or\n- The recipe has changed, or\n- if any known dependencies have changed\n\nWhen Targets Are Considered Changed\n===================================\n\nA checksum is taken on dependency targets that are existing files.\nTargets that are missing, or are directories, or whose patterns started\nwith \"!\", don't have checksums. A target is considered changed if:\n\n- The target checksum exists and has changed, or\n- There is no checksum, and the recipe has been re-run\n\nIt's a \"missing recipe\" error if there's no recipe and no checksum and\nthe target doesn't already exist.\n\nParallel Builds\n===============\n\nIf a script is called with multiple dependencies, then these\ndependencies are checked (and rebuilt if necessary) in parallel, in\nbatches of up to 8 at a time. The parallelism can be specified with the\nGM\\_THREADS environment variable.\n\nWhat To Clean\n=============\n\nYou may want to clean out all GoodMake files. After such a clean, no\nfiles will have build logs, so all encountered recipes will be run when\nupdating. This is pretty safe.\n\n::\n\n find -name '*.gm' -delete\n\nYou may want to delete all the built files. When a recipe is run, it\ncreates a build log file. You could delete all files that have an\naccompanying ``.gm`` file. This is less safe, if you have any recipes\nthat don't actually know how to create their targets. GoodMake includes\na Linux script ``goodmake_clean.sh`` to list or to clean all built\nfiles.\n\nEnvironment Variables\n=====================\n\nGoodMake uses the following environment variables:\n\n- ``LOG`` - Set logging level to ERROR, WARN (default), INFO, or DEBUG.\n- ``GM__REMAKE`` - Set to TRUE to cause all targets to be re-made.\n- ``GM__TIMEOUT`` - Number of seconds to wait for concurrency locks.\n- ``GM_THREADS`` - Set the maximum number of threads for parallel\n builds.\n- ``GM__FILE`` - Internal variable for communicating between GoodMake\n processes.\n- ``GM__STARTTIME`` - Internal variable for communicating between\n GoodMake processes.\n\nExamples\n========\n\nSpecifying an *external* file dependency\n----------------------------------------\n\nThis triggers a rebuild if someone or something changes\n``external_file``:\n\n::\n\n #? my_target\n $0 external_file\n\nThe first successful build will create a build log for ``my_target``\nwith checksums for both ``external_file`` and ``my_target``. If the\n``external_file`` changes, then it will trigger a re-run of\n``my_target`` recipe.\n\nHow to identify a *missing* dependency recipe\n---------------------------------------------\n\nIf ``nosuchdep`` doesn't exist, an error will be thrown and the build\nwill stop.\n\n::\n\n #? my_target\n $0 nosuchdep\n\nSometimes you may have an erroneous set of recipes that list a\nnon-existent dependency with no recipe to build it. If nosuchdep does\nnot exist, or is a directory, look in the logs for a message that says\n\"missing recipe\".\n\nEnsuring a recipe is *always* run\n---------------------------------\n\nThis is useful if some dependencies are not identifiable ahead of time.\nWe still want to use a checksum to see if anything has changed.\n\n::\n\n #! my_target\n ls >$1\n\nIf the pattern line starts with shebang instead of sheque, the recipe is\nalways run. ``my_target`` will only be considered changed (causing\nparent recipes to be re-run) if its checksum changes.\n\nCombining multiple *dependencies* into a single target\n------------------------------------------------------\n\nThis defines one target dependency as the equivalent of several other\ndependencies:\n\n::\n\n #? !my_prereqs\n $0 dep1 dep2 dep3\n\nSince ``!my_prereqs`` starts with a \"!\", any checksum is ignored, and so\n``my_prereqs`` will be considered changed every time the recipe is run,\nwhich will be every time the dependencies are changed.\n\nNaming a simple *script* to run from the command line\n-----------------------------------------------------\n\n::\n\n #! doit\n ls -lht\n\nThis recipe will always run, because it starts with a shebang.\n\nRefreshing after a certain amount of time\n-----------------------------------------\n\nHere are some examples of how you can trigger rebuilds from things other\nthan file changes:\n\n::\n\n #! .every-day\n touch -d yesterday .yesterday\n [ $1 -nt .yesterday ] || date>$1\n\n #! .each-boot\n touch -d $(uptime -s) .reboot\n [ $1 -nt .reboot ] || date>$1\n\n #! .each-install\n # This should work on debian systems\n FILE=$(ls -1t /var/log/installer | tail -1)\n [ $1 -nt \"$FILE\" ] || date>$1\n\n #! .each-upgrade\n [ $1 -nt /etc/lsb-release ] || date>$1\n\nA Linux script for some of the above periodic builds is included in\n``goodmake_every.sh``.\n\nDynamic lists of dependencies\n-----------------------------\n\nWith Gnu Make, it's pretty tricky to process all files in a directory,\nwhen you don't know the exact list of files ahead of time. Here's one\nway you can do it with GoodMake:\n\n::\n\n #! tgt.ls\n ls $SRC_DIR | while read file; do\n echo tgt/$file\n done > $1\n\nThis creates a ``tgt.ls`` list of the files to be built.\n\nOther Linux shell techniques are to use ``xargs`` to feed dependencies\nto ``$0``, and to use things like ``${1%.obj}`` to get source file names\nfrom target names.\n\nOther tips\n----------\n\nA useful set of Linux shell variables and functions is included in\n``goodmake_lib.sh``.\n\nGoodMake creates a ``.target.gm`` file for each successful build of\n``target``. It lists dependencies and build results in a tab-delimited\nformat.\n\nContributing\n============\n\nFeedback and contributions are welcome. GoodMake is on\n`GitHub `__ and\n`PyPI `__.\n\nLicense\n=======\n\nGoodMake is distributed under the terms of the GNU General Public\nLicense v3.0.\n", "description_content_type": "", "docs_url": null, "download_url": "", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/AmesCornish/GoodMake", "keywords": "make", "license": "GPLv3", "maintainer": "", "maintainer_email": "", "name": "goodmake", "package_url": "https://pypi.org/project/goodmake/", "platform": "", "project_url": "https://pypi.org/project/goodmake/", "project_urls": { "Homepage": "https://github.com/AmesCornish/GoodMake" }, "release_url": "https://pypi.org/project/goodmake/0.2.0/", "requires_dist": null, "requires_python": "", "summary": "A simpler build system", "version": "0.2.0" }, "last_serial": 4479900, "releases": { "0.1.0": [ { "comment_text": "", "digests": { "md5": "67b93732cbe6384a61e3b0b19e6553bc", "sha256": "de3dbb8691c8e3ed05eef0c61128ce58b14bb0377c30b51cd716f248b6e7c897" }, "downloads": -1, "filename": "goodmake-0.1.0-py3-none-any.whl", "has_sig": false, "md5_digest": "67b93732cbe6384a61e3b0b19e6553bc", "packagetype": "bdist_wheel", "python_version": "3.6", "requires_python": null, "size": 15726, "upload_time": "2018-06-28T05:41:47", "url": "https://files.pythonhosted.org/packages/75/df/b9c8fb46e6a507d872e7bc3219329adeec49228285b4f5b2ac3c10c83755/goodmake-0.1.0-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "d26d440315d942129a4d880ab7f053aa", "sha256": "1bddfe6aa06d1b84be2780475b12a189ee2ef6026053791d7fdb669cd52c1b94" }, "downloads": -1, "filename": "goodmake-0.1.0.tar.gz", "has_sig": false, "md5_digest": "d26d440315d942129a4d880ab7f053aa", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 13186, "upload_time": "2018-06-28T05:41:45", "url": "https://files.pythonhosted.org/packages/a1/90/04ac258ca07c86ddba97f02fad263e61161b45246fe7ca259aef5e36dc94/goodmake-0.1.0.tar.gz" } ], "0.1.2": [ { "comment_text": "", "digests": { "md5": "1e5f7d2bb4df165733f1e48d4a6b81d1", "sha256": "67494d5d9630d16d0d61ab562b7c9a546fc103d569f63f45361f3b79fd3295a6" }, "downloads": -1, "filename": "goodmake-0.1.2-py3-none-any.whl", "has_sig": false, "md5_digest": "1e5f7d2bb4df165733f1e48d4a6b81d1", "packagetype": "bdist_wheel", "python_version": "3.6", "requires_python": null, "size": 8939, "upload_time": "2018-09-30T23:03:03", "url": "https://files.pythonhosted.org/packages/2f/27/a2a2f02af174e37111489c3fc1473973670dae417d5c5be67349ce1c2c74/goodmake-0.1.2-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "f696977af0e7356545e695f982de1659", "sha256": "638e3ba183cddedb2e952a51b5d1b24a40a9aecac54c5eca46101e315d110cc6" }, "downloads": -1, "filename": "goodmake-0.1.2.tar.gz", "has_sig": false, "md5_digest": "f696977af0e7356545e695f982de1659", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 22569, "upload_time": "2018-09-30T23:03:01", "url": "https://files.pythonhosted.org/packages/e3/a2/1acd07a699061705206b53e5873f78867c68036712a3858c8330699bc07f/goodmake-0.1.2.tar.gz" } ], "0.1.4": [ { "comment_text": "", "digests": { "md5": "f6129024ef8070b28d9231507c7ba7d2", "sha256": "ae89b3c108f916a211cf873737a282d4569903ec3c08c44968eac21045062586" }, "downloads": -1, "filename": "goodmake-0.1.4-py3-none-any.whl", "has_sig": false, "md5_digest": "f6129024ef8070b28d9231507c7ba7d2", "packagetype": "bdist_wheel", "python_version": "3.6", "requires_python": null, "size": 9188, "upload_time": "2018-10-17T15:09:38", "url": "https://files.pythonhosted.org/packages/ac/cb/866789631a179bfff00d22999d618f1ff0daf63c2dfc695cb3ef237b2ab6/goodmake-0.1.4-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "72ca9de3ad580bd20bda1669a36543e8", "sha256": "4dcd0b8dba44046faa6f99fa8799e1c386f4620191428efbc5c253890def6afb" }, "downloads": -1, "filename": "goodmake-0.1.4.tar.gz", "has_sig": false, "md5_digest": "72ca9de3ad580bd20bda1669a36543e8", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 22806, "upload_time": "2018-10-17T15:09:36", "url": "https://files.pythonhosted.org/packages/13/46/073562745e41bb8ad1e343694028cdb603bc2919e3bd9b902396a4817e5e/goodmake-0.1.4.tar.gz" } ], "0.1.5": [ { "comment_text": "", "digests": { "md5": "61e71dbf41bb2773f8bbaa6408d71042", "sha256": "b7c598c6609bc64fa14f108ae04daa4dc28dac47c09db1ac26243cd372bcd607" }, "downloads": -1, "filename": "goodmake-0.1.5-py3-none-any.whl", "has_sig": false, "md5_digest": "61e71dbf41bb2773f8bbaa6408d71042", "packagetype": "bdist_wheel", "python_version": "3.6", "requires_python": null, "size": 9861, "upload_time": "2018-11-10T19:07:51", "url": "https://files.pythonhosted.org/packages/7c/c5/63b06fa765e7474ae124776c0816306db9746859f256ff50c72dce1f332a/goodmake-0.1.5-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "7f02d516d21a7621d86601edac6a9835", "sha256": "2caca912122cfdbef072095c249e25287ec1aee758cd22ec5355560ad33bc3df" }, "downloads": -1, "filename": "goodmake-0.1.5.tar.gz", "has_sig": false, "md5_digest": "7f02d516d21a7621d86601edac6a9835", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 23454, "upload_time": "2018-11-10T19:07:49", "url": "https://files.pythonhosted.org/packages/c8/3a/57c5e2084c793aaa59f3d33a5f26dee39d3467eac07a93c3c8abcded91a0/goodmake-0.1.5.tar.gz" } ], "0.2.0": [ { "comment_text": "", "digests": { "md5": "ac645724871141a0ad6b6aae90e1cb7b", "sha256": "ae3a7196b134eb88fd5d18dbfa57505c05b32dee843493bbb3921a239d6e9dd5" }, "downloads": -1, "filename": "goodmake-0.2.0-py3-none-any.whl", "has_sig": false, "md5_digest": "ac645724871141a0ad6b6aae90e1cb7b", "packagetype": "bdist_wheel", "python_version": "3.6", "requires_python": null, "size": 18907, "upload_time": "2018-11-13T01:03:44", "url": "https://files.pythonhosted.org/packages/69/2d/201c960921b989d5fc74f832a12454c59e10daa86c5a56e10d469fb1bb0e/goodmake-0.2.0-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "913a988b272b5646b2d6369ccfcb1227", "sha256": "ff15c253d233fbe2624169bf31f5f5bf2b3c4eda633cd7a02b24116b9582dd1e" }, "downloads": -1, "filename": "goodmake-0.2.0.tar.gz", "has_sig": false, "md5_digest": "913a988b272b5646b2d6369ccfcb1227", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 34044, "upload_time": "2018-11-13T01:03:42", "url": "https://files.pythonhosted.org/packages/19/d9/ae07b8e7a608e8725cef0197fd6c2c16a956904525c3aea740a0f2527edb/goodmake-0.2.0.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "ac645724871141a0ad6b6aae90e1cb7b", "sha256": "ae3a7196b134eb88fd5d18dbfa57505c05b32dee843493bbb3921a239d6e9dd5" }, "downloads": -1, "filename": "goodmake-0.2.0-py3-none-any.whl", "has_sig": false, "md5_digest": "ac645724871141a0ad6b6aae90e1cb7b", "packagetype": "bdist_wheel", "python_version": "3.6", "requires_python": null, "size": 18907, "upload_time": "2018-11-13T01:03:44", "url": "https://files.pythonhosted.org/packages/69/2d/201c960921b989d5fc74f832a12454c59e10daa86c5a56e10d469fb1bb0e/goodmake-0.2.0-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "913a988b272b5646b2d6369ccfcb1227", "sha256": "ff15c253d233fbe2624169bf31f5f5bf2b3c4eda633cd7a02b24116b9582dd1e" }, "downloads": -1, "filename": "goodmake-0.2.0.tar.gz", "has_sig": false, "md5_digest": "913a988b272b5646b2d6369ccfcb1227", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 34044, "upload_time": "2018-11-13T01:03:42", "url": "https://files.pythonhosted.org/packages/19/d9/ae07b8e7a608e8725cef0197fd6c2c16a956904525c3aea740a0f2527edb/goodmake-0.2.0.tar.gz" } ] }