From b1c3d150fe37a3f2c158ad7a90a2ae33ef95c218 Mon Sep 17 00:00:00 2001 From: Nicolas Schneider Date: Tue, 1 Jan 2019 23:31:41 +0100 Subject: [PATCH 1/2] vs: always consider run, install, and test targets out-of-date Pre/PostBuildEvents do not run if no other build steps are out-of-date. For most run targets (including install and test) that have no other build steps, VS considers these to be always up-to-date after they have been built once. On the other hand, CustomBuild has clearly defined inputs and outputs that define whether the target is up-to-date or not. By using a nonexistent file as output of CustomBuild, it is always considered out-of-date. This aligns the VS behavior with ninja. `ninja install` unconditionally installs, `ninja test` always runs the tests, and a run target always gets executed, without any checks whether it is up-to-date or not. --- mesonbuild/backend/vs2010backend.py | 49 ++++++++++++++--------------- 1 file changed, 23 insertions(+), 26 deletions(-) diff --git a/mesonbuild/backend/vs2010backend.py b/mesonbuild/backend/vs2010backend.py index 8029d58c2..e0bbf2758 100644 --- a/mesonbuild/backend/vs2010backend.py +++ b/mesonbuild/backend/vs2010backend.py @@ -494,7 +494,6 @@ class Vs2010Backend(backends.Backend): def gen_run_target_vcxproj(self, target, ofname, guid): root = self.create_basic_crap(target, guid) action = ET.SubElement(root, 'ItemDefinitionGroup') - customstep = ET.SubElement(action, 'PostBuildEvent') cmd_raw = [target.command] + target.args cmd = python_command + \ [os.path.join(self.environment.get_script_dir(), 'commandrunner.py'), @@ -515,8 +514,7 @@ class Vs2010Backend(backends.Backend): else: cmd.append(i) cmd_templ = '''"%s" ''' * len(cmd) - ET.SubElement(customstep, 'Command').text = cmd_templ % tuple(cmd) - ET.SubElement(customstep, 'Message').text = 'Running custom command.' + self.add_custom_build(root, 'run_target', cmd_templ % tuple(cmd)) ET.SubElement(root, 'Import', Project=r'$(VCTargetsPath)\Microsoft.Cpp.targets') self.add_regen_dependency(root) self._prettyprint_vcxproj_xml(ET.ElementTree(root), ofname) @@ -1343,26 +1341,14 @@ if %%errorlevel%% neq 0 goto :VCEnd''' ET.SubElement(midl, 'TypeLibraryName').text = '%(Filename).tlb' ET.SubElement(midl, 'InterfaceIdentifierFilename').text = '%(Filename)_i.c' ET.SubElement(midl, 'ProxyFileName').text = '%(Filename)_p.c' - postbuild = ET.SubElement(action, 'PostBuildEvent') - ET.SubElement(postbuild, 'Message') # FIXME: No benchmarks? test_command = self.environment.get_build_command() + ['test', '--no-rebuild'] if not self.environment.coredata.get_builtin_option('stdsplit'): test_command += ['--no-stdsplit'] if self.environment.coredata.get_builtin_option('errorlogs'): test_command += ['--print-errorlogs'] - cmd_templ = '''setlocal -"%s" -if %%errorlevel%% neq 0 goto :cmEnd -:cmEnd -endlocal & call :cmErrorLevel %%errorlevel%% & goto :cmDone -:cmErrorLevel -exit /b %%1 -:cmDone -if %%errorlevel%% neq 0 goto :VCEnd''' self.serialize_tests() - ET.SubElement(postbuild, 'Command').text =\ - cmd_templ % ('" "'.join(test_command)) + self.add_custom_build(root, 'run_tests', '"%s"' % ('" "'.join(test_command))) ET.SubElement(root, 'Import', Project=r'$(VCTargetsPath)\Microsoft.Cpp.targets') self.add_regen_dependency(root) self._prettyprint_vcxproj_xml(ET.ElementTree(root), ofname) @@ -1417,12 +1403,21 @@ if %%errorlevel%% neq 0 goto :VCEnd''' ET.SubElement(midl, 'TypeLibraryName').text = '%(Filename).tlb' ET.SubElement(midl, 'InterfaceIdentifierFilename').text = '%(Filename)_i.c' ET.SubElement(midl, 'ProxyFileName').text = '%(Filename)_p.c' - postbuild = ET.SubElement(action, 'PostBuildEvent') - ET.SubElement(postbuild, 'Message') - # FIXME: No benchmarks? - test_command = self.environment.get_build_command() + ['install', '--no-rebuild'] + install_command = self.environment.get_build_command() + ['install', '--no-rebuild'] + self.add_custom_build(root, 'run_install', '"%s"' % ('" "'.join(install_command))) + ET.SubElement(root, 'Import', Project=r'$(VCTargetsPath)\Microsoft.Cpp.targets') + self.add_regen_dependency(root) + self._prettyprint_vcxproj_xml(ET.ElementTree(root), ofname) + + def add_custom_build(self, node, rulename, command, output_file=None): + igroup = ET.SubElement(node, 'ItemGroup') + rulefile = os.path.join(self.environment.get_scratch_dir(), rulename + '.rule') + if not os.path.exists(rulefile): + with open(rulefile, 'w', encoding='utf-8') as f: + f.write("# Meson regen file.") + custombuild = ET.SubElement(igroup, 'CustomBuild', Include=rulefile) cmd_templ = '''setlocal -"%s" +%s if %%errorlevel%% neq 0 goto :cmEnd :cmEnd endlocal & call :cmErrorLevel %%errorlevel%% & goto :cmDone @@ -1430,11 +1425,13 @@ endlocal & call :cmErrorLevel %%errorlevel%% & goto :cmDone exit /b %%1 :cmDone if %%errorlevel%% neq 0 goto :VCEnd''' - ET.SubElement(postbuild, 'Command').text =\ - cmd_templ % ('" "'.join(test_command)) - ET.SubElement(root, 'Import', Project=r'$(VCTargetsPath)\Microsoft.Cpp.targets') - self.add_regen_dependency(root) - self._prettyprint_vcxproj_xml(ET.ElementTree(root), ofname) + ET.SubElement(custombuild, 'Command').text = cmd_templ % command + if not output_file: + # Use a nonexistent file to always consider the target out-of-date. + output_file = os.path.join(self.environment.get_scratch_dir(), 'outofdate.file') + while os.path.exists(output_file): + output_file += '0' + ET.SubElement(custombuild, 'Outputs').text = output_file def generate_debug_information(self, link): # valid values for vs2015 is 'false', 'true', 'DebugFastLink' From e004e711b1220ad0d94f91840a115713b594e727 Mon Sep 17 00:00:00 2001 From: Nicolas Schneider Date: Sat, 26 Jan 2019 10:57:31 +0100 Subject: [PATCH 2/2] vs: refactor regen target creation --- mesonbuild/backend/vs2010backend.py | 44 +++++++++++------------------ 1 file changed, 16 insertions(+), 28 deletions(-) diff --git a/mesonbuild/backend/vs2010backend.py b/mesonbuild/backend/vs2010backend.py index e0bbf2758..61484741e 100644 --- a/mesonbuild/backend/vs2010backend.py +++ b/mesonbuild/backend/vs2010backend.py @@ -1263,31 +1263,13 @@ class Vs2010Backend(backends.Backend): ET.SubElement(midl, 'InterfaceIdentifierFilename').text = '%(Filename)_i.c' ET.SubElement(midl, 'ProxyFileName').text = '%(Filename)_p.c' regen_command = self.environment.get_build_command() + ['--internal', 'regencheck'] - private_dir = self.environment.get_scratch_dir() - vcvars_command = self.get_vcvars_command() - cmd_templ = '''setlocal -call %s > NUL -"%s" "%s" -if %%errorlevel%% neq 0 goto :cmEnd -:cmEnd -endlocal & call :cmErrorLevel %%errorlevel%% & goto :cmDone -:cmErrorLevel -exit /b %%1 -:cmDone -if %%errorlevel%% neq 0 goto :VCEnd''' - igroup = ET.SubElement(root, 'ItemGroup') - rulefile = os.path.join(self.environment.get_scratch_dir(), 'regen.rule') - if not os.path.exists(rulefile): - with open(rulefile, 'w', encoding='utf-8') as f: - f.write("# Meson regen file.") - custombuild = ET.SubElement(igroup, 'CustomBuild', Include=rulefile) - message = ET.SubElement(custombuild, 'Message') - message.text = 'Checking whether solution needs to be regenerated.' - ET.SubElement(custombuild, 'Command').text = cmd_templ % \ - (vcvars_command, '" "'.join(regen_command), private_dir) - ET.SubElement(custombuild, 'Outputs').text = Vs2010Backend.get_regen_stampfile(self.environment.get_build_dir()) - deps = self.get_regen_filelist() - ET.SubElement(custombuild, 'AdditionalInputs').text = ';'.join(deps) + cmd_templ = '''call %s > NUL +"%s" "%s"''' + regen_command = cmd_templ % \ + (self.get_vcvars_command(), '" "'.join(regen_command), self.environment.get_scratch_dir()) + self.add_custom_build(root, 'regen', regen_command, deps=self.get_regen_filelist(), + outputs=[Vs2010Backend.get_regen_stampfile(self.environment.get_build_dir())], + msg='Checking whether solution needs to be regenerated.') ET.SubElement(root, 'Import', Project=r'$(VCTargetsPath)\Microsoft.Cpp.targets') ET.SubElement(root, 'ImportGroup', Label='ExtensionTargets') self._prettyprint_vcxproj_xml(ET.ElementTree(root), ofname) @@ -1409,13 +1391,16 @@ if %%errorlevel%% neq 0 goto :VCEnd''' self.add_regen_dependency(root) self._prettyprint_vcxproj_xml(ET.ElementTree(root), ofname) - def add_custom_build(self, node, rulename, command, output_file=None): + def add_custom_build(self, node, rulename, command, deps=None, outputs=None, msg=None): igroup = ET.SubElement(node, 'ItemGroup') rulefile = os.path.join(self.environment.get_scratch_dir(), rulename + '.rule') if not os.path.exists(rulefile): with open(rulefile, 'w', encoding='utf-8') as f: f.write("# Meson regen file.") custombuild = ET.SubElement(igroup, 'CustomBuild', Include=rulefile) + if msg: + message = ET.SubElement(custombuild, 'Message') + message.text = msg cmd_templ = '''setlocal %s if %%errorlevel%% neq 0 goto :cmEnd @@ -1426,12 +1411,15 @@ exit /b %%1 :cmDone if %%errorlevel%% neq 0 goto :VCEnd''' ET.SubElement(custombuild, 'Command').text = cmd_templ % command - if not output_file: + if not outputs: # Use a nonexistent file to always consider the target out-of-date. output_file = os.path.join(self.environment.get_scratch_dir(), 'outofdate.file') while os.path.exists(output_file): output_file += '0' - ET.SubElement(custombuild, 'Outputs').text = output_file + outputs = [output_file] + ET.SubElement(custombuild, 'Outputs').text = ';'.join(outputs) + if deps: + ET.SubElement(custombuild, 'AdditionalInputs').text = ';'.join(deps) def generate_debug_information(self, link): # valid values for vs2015 is 'false', 'true', 'DebugFastLink'