Bug 1260299 - Use FileAvoidWrite when writing Visual Studio files; r?chmanchester draft
authorGregory Szorc <gps@mozilla.com>
Thu, 19 May 2016 21:11:36 -0700
changeset 369054 11e6fa78f7418de9ac77da2f3fe15e0633991562
parent 369053 95779ee5d5babde81e3185747b7b7e2050e94775
child 369055 094e9f08b014a421e3b8f34528be6e65870e4534
push id18715
push userbmo:gps@mozilla.com
push dateFri, 20 May 2016 04:59:11 +0000
reviewerschmanchester
bugs1260299
milestone49.0a1
Bug 1260299 - Use FileAvoidWrite when writing Visual Studio files; r?chmanchester By using self._write_file() and FileAvoidWrite, we avoid writing files unless they change. This means that Visual Studio won't want you to reload the solution and projects whenever the backend is generated. This means you can regenerate the backend all you want and chances are it won't disrupt your Visual Studio experience. Since self._write_file() creates parent directories for us, we were able to remove this code. If you run `mach build-backend --diff` with this commit, output will change. For reasons I don't understand, we were producing XML with e.g. \r\r\n sequences. This patch appears to restore \r\n. How we got \r\r I don't know because I can't find anywhere in the code where that can occur. But this commit does appear to restore sanity. Also, it appears modern versions of Visual Studio (perhaps only VS2015) doesn't write your project files. When I initially implemented Visual Studio project generation several years ago, as soon as you loaded the solution and hit "Save All" Visual Studio would re-save your files using a slightly different formatting (it did some gnarly things with XML indentation). VS2015 doesn't do this. So your files on disk should be unmodified for longer, making Visual Studio a more viable development environment. Yay. MozReview-Commit-ID: 7CSk0dsLOli
python/mozbuild/mozbuild/backend/visualstudio.py
--- a/python/mozbuild/mozbuild/backend/visualstudio.py
+++ b/python/mozbuild/mozbuild/backend/visualstudio.py
@@ -56,21 +56,16 @@ def visual_studio_product_to_platform_to
 class VisualStudioBackend(CommonBackend):
     """Generate Visual Studio project files.
 
     This backend is used to produce Visual Studio projects and a solution
     to foster developing Firefox with Visual Studio.
 
     This backend is currently considered experimental. There are many things
     not optimal about how it works.
-
-    It's worth noting that lots of file I/O here is not using
-    self._write_file(). That's because we need Windows line endings preserved
-    and self._write_file() currently opens files in text mode, which behaves
-    oddly under MozillaBuild.
     """
 
     def _init(self):
         CommonBackend._init(self)
 
         # These should eventually evolve into parameters.
         self._out_dir = os.path.join(self.environment.topobjdir, 'msvc')
         self._projsubdir = 'projects'
@@ -137,26 +132,16 @@ class VisualStudioBackend(CommonBackend)
         reldir = getattr(obj, 'relativedir', None)
 
         s = self._paths_to_sources.setdefault(reldir, set())
         s.update(obj.files)
 
     def consume_finished(self):
         out_dir = self._out_dir
         out_proj_dir = os.path.join(self._out_dir, self._projsubdir)
-        try:
-            os.makedirs(out_dir)
-        except OSError as e:
-            if e.errno != errno.EEXIST:
-                raise
-        try:
-            os.makedirs(out_proj_dir)
-        except OSError as e:
-            if e.errno != errno.EEXIST:
-                raise
 
         projects = self._write_projects_for_sources(self._libs_to_paths,
             "library", out_proj_dir)
         projects.update(self._write_projects_for_sources(self._progs_to_paths,
             "binary", out_proj_dir))
 
         # Generate projects that can be used to build common targets.
         for target in ('export', 'binaries', 'tools', 'full'):
@@ -174,34 +159,34 @@ class VisualStudioBackend(CommonBackend)
         # A project that can be used to regenerate the visual studio projects.
         basename = 'target_vs'
         project_id = self._write_vs_project(out_proj_dir, basename, 'visual-studio',
             build_command='$(SolutionDir)\\mach.bat build-backend -b VisualStudio')
         projects[basename] = (project_id, basename, 'visual-studio')
 
         # Write out a shared property file with common variables.
         props_path = os.path.join(out_proj_dir, 'mozilla.props')
-        with open(props_path, 'wb') as fh:
+        with self._write_file(props_path, mode='rb') as fh:
             self._write_props(fh)
 
         # Generate some wrapper scripts that allow us to invoke mach inside
         # a MozillaBuild-like environment. We currently only use the batch
         # script. We'd like to use the PowerShell script. However, it seems
         # to buffer output from within Visual Studio (surely this is
         # configurable) and the default execution policy of PowerShell doesn't
         # allow custom scripts to be executed.
-        with open(os.path.join(out_dir, 'mach.bat'), 'wb') as fh:
+        with self._write_file(os.path.join(out_dir, 'mach.bat'), mode='rb') as fh:
             self._write_mach_batch(fh)
 
-        with open(os.path.join(out_dir, 'mach.ps1'), 'wb') as fh:
+        with self._write_file(os.path.join(out_dir, 'mach.ps1'), mode='rb') as fh:
             self._write_mach_powershell(fh)
 
         # Write out a solution file to tie it all together.
         solution_path = os.path.join(out_dir, 'mozilla.sln')
-        with open(solution_path, 'wb') as fh:
+        with self._write_file(solution_path, mode='rb') as fh:
             self._write_solution(fh, projects)
 
     def _write_projects_for_sources(self, sources, prefix, out_dir):
         projects = {}
         for item, path in sorted(sources.items()):
             config = self._paths_to_configs.get(path, None)
             sources = self._paths_to_sources.get(path, set())
             sources = set(os.path.join('$(TopSrcDir)', path, s) for s in sources)
@@ -450,21 +435,21 @@ class VisualStudioBackend(CommonBackend)
         # appropriate build tool.
         fh.write(b'"%%MOZILLABUILD%%\\msys\\bin\\bash" '
             b'-c "%s/mach --log-no-times %%1 %%2 %%3 %%4 %%5 %%6 %%7"' % relpath)
 
     def _write_vs_project(self, out_dir, basename, name, **kwargs):
         root = '%s.vcxproj' % basename
         project_id = get_id(basename.encode('utf-8'))
 
-        with open(os.path.join(out_dir, root), 'wb') as fh:
+        with self._write_file(os.path.join(out_dir, root), mode='rb') as fh:
             project_id, name = VisualStudioBackend.write_vs_project(fh,
                 self._version, project_id, name, **kwargs)
 
-        with open(os.path.join(out_dir, '%s.user' % root), 'w') as fh:
+        with self._write_file(os.path.join(out_dir, '%s.user' % root), mode='rb') as fh:
             fh.write('<?xml version="1.0" encoding="utf-8"?>\r\n')
             fh.write('<Project ToolsVersion="4.0" xmlns="%s">\r\n' %
                 MSBUILD_NAMESPACE)
             fh.write('</Project>\r\n')
 
         return project_id
 
     @staticmethod