diff --git a/bootstrap.py b/bootstrap.py index 8c0687e..abd2528 100755 --- a/bootstrap.py +++ b/bootstrap.py @@ -68,6 +68,8 @@ for src in glob.glob('src/*.cc'): else: if src.endswith('-win32.cc'): continue + if '_main' in src: + continue sources.append(src) @@ -110,13 +112,41 @@ verbose = [] if options.verbose: verbose = ['-v'] -print 'Building ninja using itself...' -run([sys.executable, 'configure.py'] + conf_args) -run(['./' + binary] + verbose) -os.unlink(binary) - if sys.platform.startswith('win32'): + # Build ninja-msvc-helper using ninja without an msvc-helper. + print 'Building ninja-msvc-helper...' + run([sys.executable, 'configure.py', '--with-msvc-helper='] + conf_args) + run(['./' + binary] + verbose + ['ninja-msvc-helper']) + + # Rename the helper to the same name + .bootstrap. + helper_binary = 'ninja-msvc-helper.bootstrap.exe' + try: + os.unlink(helper_binary) + except: + pass + os.rename('ninja-msvc-helper.exe', helper_binary) + + # Build ninja using the newly-built msvc-helper. + print 'Building ninja using itself...' + run([sys.executable, 'configure.py', + '--with-msvc-helper=%s' % helper_binary] + conf_args) + run(['./' + binary] + verbose) + + # Clean up. for obj in glob.glob('*.obj'): os.unlink(obj) -print 'Done!' + print """ +Done! + +Note: to work around Windows file locking, where you can't rebuild an +in-use binary, to run ninja after making any changes to build ninja itself +you should run ninja.bootstrap instead. Your build is also configured to +use ninja-msvc-helper.bootstrap.exe instead of the ninja-msvc-helper.exe +that it builds; see the --help output of configure.py.""" +else: + print 'Building ninja using itself...' + run([sys.executable, 'configure.py'] + conf_args) + run(['./' + binary] + verbose) + os.unlink(binary) + print 'Done!' diff --git a/configure.py b/configure.py index bdf4613..981d401 100755 --- a/configure.py +++ b/configure.py @@ -45,6 +45,8 @@ parser.add_option('--with-gtest', metavar='PATH', parser.add_option('--with-python', metavar='EXE', help='use EXE as the Python interpreter', default=os.path.basename(sys.executable)) +parser.add_option('--with-msvc-helper', metavar='NAME', + help="name for ninja-msvc-helper binary (MSVC only)") (options, args) = parser.parse_args() if args: print 'ERROR: extra unparsed command-line arguments:', args @@ -177,8 +179,11 @@ n.variable('ldflags', ' '.join(shell_escape(flag) for flag in ldflags)) n.newline() if platform == 'windows': + compiler = '$cxx' + if options.with_msvc_helper: + compiler = '%s -o $out -- $cxx /showIncludes' % options.with_msvc_helper n.rule('cxx', - command='$cxx $cflags -c $in /Fo$out', + command='%s $cflags -c $in /Fo$out' % compiler, depfile='$out.d', description='CXX $out') else: @@ -282,6 +287,16 @@ ninja = n.build(binary('ninja'), 'link', objs, implicit=ninja_lib, n.newline() all_targets += ninja +if platform == 'windows': + n.comment('Helper for working with MSVC.') + msvc_helper = n.build(binary('ninja-msvc-helper'), 'link', + cxx('msvc_helper_main-win32'), + implicit=ninja_lib, + variables=[('libs', libs)]) + n.default(msvc_helper) + n.newline() + all_targets += msvc_helper + n.comment('Tests all build into ninja_test executable.') variables = [] @@ -397,7 +412,6 @@ if host != 'mingw': implicit=['configure.py', os.path.normpath('misc/ninja_syntax.py')]) n.newline() -n.comment('Build only the main binary by default.') n.default(ninja) n.newline() diff --git a/src/msvc_helper_main-win32.cc b/src/msvc_helper_main-win32.cc new file mode 100644 index 0000000..f265010 --- /dev/null +++ b/src/msvc_helper_main-win32.cc @@ -0,0 +1,115 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "msvc_helper.h" + +#include + +#include "util.h" + +#include "getopt.h" + +namespace { + +void Usage() { + printf( +"ninja-msvc-helper: adjust msvc command-line tools for use by ninja.\n" +"\n" +"usage: ninja-mvsc-helper [options] -- command args\n" +"options:\n" +" -e ENVFILE load environment block from ENVFILE as environment\n" +" -r BASE normalize paths and make relative to BASE before output\n" +" -o FILE write output dependency information to FILE.d\n" + ); +} + +void PushPathIntoEnvironment(const string& env_block) { + const char* as_str = env_block.c_str(); + while (as_str[0]) { + if (_strnicmp(as_str, "path=", 5) == 0) { + _putenv(as_str); + return; + } else { + as_str = &as_str[strlen(as_str) + 1]; + } + } +} + +} // anonymous namespace + +int main(int argc, char** argv) { + const char* output_filename = NULL; + const char* relative_to = NULL; + const char* envfile = NULL; + + const option kLongOptions[] = { + { "help", no_argument, NULL, 'h' }, + { NULL, 0, NULL, 0 } + }; + int opt; + while ((opt = getopt_long(argc, argv, "e:o:r:h", kLongOptions, NULL)) != -1) { + switch (opt) { + case 'e': + envfile = optarg; + break; + case 'o': + output_filename = optarg; + break; + case 'r': + relative_to = optarg; + break; + case 'h': + default: + Usage(); + return 0; + } + } + + if (!output_filename) + Fatal("-o required"); + + string env; + if (envfile) { + string err; + if (ReadFile(envfile, &env, &err) != 0) + Fatal("couldn't open %s: %s", envfile, err.c_str()); + PushPathIntoEnvironment(env); + } + + char* command = GetCommandLine(); + command = strstr(command, " -- "); + if (!command) { + Fatal("expected command line to end with \" -- command args\""); + } + command += 4; + + CLWrapper cl; + if (!env.empty()) + cl.SetEnvBlock((void*)env.data()); + int exit_code = cl.Run(command); + + string depfile = string(output_filename) + ".d"; + FILE* output = fopen(depfile.c_str(), "w"); + if (!output) { + Fatal("opening %s: %s", depfile.c_str(), GetLastErrorString().c_str()); + } + fprintf(output, "%s: ", output_filename); + for (vector::iterator i = cl.includes_.begin(); + i != cl.includes_.end(); ++i) { + fprintf(output, "%s\n", i->c_str()); + } + fclose(output); + + return exit_code; +}