├── LICENSE ├── README.md └── komake /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Kazuho Oku 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # komake 2 | 3 | Komake is a wrapper of make(1) that limits the concurrency of makes being recursively spawned. 4 | 5 | ## Synopsis 6 | 7 | ``` 8 | $ komake # runs `make all` 9 | $ komake -j4 myproj # options are passsed to `make` 10 | $ MAKE=my-make komake # use my favorite make, wrapped by komake 11 | ``` 12 | 13 | ## Description 14 | 15 | While working on large projects, in order to reduce build time, it is generally preferable to use the `-j` option so that make (1) spawns multiple sub-processes concurrently. When there are sub-projects, the main Makefile is typically written so that sub-processes, each running make (1), will be spawned 16 | for building each of those sub-projects. 17 | 18 | The problem is that there might be a diamond dependency between those sub-projects (e.g., sub-project-1 and sub-project-2 depending on sub-project-3), and that make (1) does not have the capability to prevent the sub-project from being built simultanenously (i.e. `make sub-project-3` invoked more than once). When race happens, the result of the build process becomes unpredictable, often ending up in an error. 19 | 20 | Komake is a wrapper for make (1) that resolves this problem, by limiting the concurrency of make (1) building sub-projects to exactly one. 21 | 22 | ## Setting Concurrency 23 | 24 | In order to achieve the necessary concurrency, users are advised to set the argument of `-j` option to the number of CPU cores plus the number of make (1) processes that may be invoked. This is because make (1) processes being delayed due to the concurrency limitation counts towards the concurrency as well. 25 | 26 | ## Notes 27 | 28 | While komake is meant to work, Japanese users should not assume that komake is the path to victory. Read the name carefully. 29 | -------------------------------------------------------------------------------- /komake: -------------------------------------------------------------------------------- 1 | #! /usr/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | use Fcntl qw(LOCK_EX); 6 | use File::Temp qw(tempdir); 7 | 8 | if (!defined $ENV{KOMAKE_LEVEL}) { 9 | # This is the root process, initialize, and run make as subprocess. 10 | 11 | # set or update the MAKE environment variable 12 | $ENV{MAKE} = "make" 13 | unless defined $ENV{MAKE}; 14 | $ENV{KOMAKE_REALMAKE} = $ENV{MAKE}; 15 | $ENV{MAKE} = $0; 16 | 17 | # setup our bookkeeping 18 | $ENV{KOMAKE_LEVEL} = 0; 19 | $ENV{KOMAKE_DIR} = tempdir(CLEANUP => 1); 20 | 21 | system($ENV{KOMAKE_REALMAKE}, @ARGV) == 0 22 | or die "make failed:$?"; 23 | exit 0; 24 | } 25 | 26 | # Child process that we need to control concurrency. We do so by limiting the number of make processes running at each level to one 27 | 28 | ++$ENV{KOMAKE_LEVEL}; 29 | 30 | # do not let exec close the lockfile 31 | $^F = 99999; 32 | 33 | # obtain lock 34 | my $lockfn = "$ENV{KOMAKE_DIR}/$ENV{KOMAKE_LEVEL}"; 35 | open my $fh, ">>", $lockfn 36 | or die "failed to open $lockfn:$!"; 37 | flock $fh, LOCK_EX 38 | or die "failed to lock file $lockfn:$!"; 39 | 40 | exec $ENV{KOMAKE_REALMAKE}, @ARGV; 41 | die "failed to exec make:$?"; 42 | --------------------------------------------------------------------------------