Add long path support for windows Change-Id: I84a25207a08e8590288855c757b0241c4a8a9fed Reviewed-on: https://gn-review.googlesource.com/c/gn/+/16600 Reviewed-by: David Turner <digit@google.com> Reviewed-by: Takuto Ikuta <tikuta@google.com> Commit-Queue: Takuto Ikuta <tikuta@google.com>
diff --git a/AUTHORS b/AUTHORS index 5e1e5e6..f7f74c1 100644 --- a/AUTHORS +++ b/AUTHORS
@@ -49,5 +49,6 @@ Tripta Gupta <tripta.g@samsung.com> Wink Saville <wink@saville.com> Yuriy Taraday <yorik.sar@gmail.com> +Zhongwei Wang <carolwolfking@gmail.com> Oleksandr Motsok <boramaabak@gmail.com> Ihor Karavan <ihorkaravan96@gmail.com>
diff --git a/build/gen.py b/build/gen.py index 07b699b..5250954 100755 --- a/build/gen.py +++ b/build/gen.py
@@ -148,7 +148,11 @@ result.append('%s=%s' % (long_option, item)) else: assert action is None, "Unsupported action " + action - return ' '.join(shell_quote(item) for item in result) + + if platform.system() == "Windows": + return ' '.join(result) + else: + return ' '.join(shell_quote(item) for item in result) def main(argv): @@ -175,7 +179,8 @@ help='Enable the use of UndefinedBehaviorSanitizer') args_list.add('--no-last-commit-position', action='store_true', help='Do not generate last_commit_position.h.') - args_list.add('--out-path', + args_list.add('--out-path', type=str, + default=os.path.join(REPO_ROOT, 'out'), help='The path to generate the build files in.') args_list.add('--no-strip', action='store_true', help='Don\'t strip release build. Useful for profiling.') @@ -214,7 +219,7 @@ else: host = platform - out_dir = options.out_path or os.path.join(REPO_ROOT, 'out') + out_dir = options.out_path if not os.path.isdir(out_dir): os.makedirs(out_dir) if not options.no_last_commit_position: @@ -566,7 +571,10 @@ '/D_HAS_EXCEPTIONS=0', ]) - ldflags.extend(['/DEBUG', '/MACHINE:x64']) + win_manifest = os.path.relpath( + os.path.join(REPO_ROOT, "build/windows.manifest.xml"), options.out_path) + ldflags.extend(['/DEBUG', '/MACHINE:x64', '/MANIFEST:EMBED', + f'/MANIFESTINPUT:{win_manifest}']) static_libraries = { 'base': {'sources': [
diff --git a/build/windows.manifest.xml b/build/windows.manifest.xml new file mode 100644 index 0000000..6058750 --- /dev/null +++ b/build/windows.manifest.xml
@@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8" standalone="yes"?> +<!-- +This is a Windows application manifest file. +See: https://docs.microsoft.com/en-us/windows/win32/sbscs/application-manifests +--> +<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3"> + <!-- Remove (most) legacy path limits --> + <asmv3:application> + <asmv3:windowsSettings xmlns:ws2="http://schemas.microsoft.com/SMI/2016/WindowsSettings"> + <ws2:longPathAware>true</ws2:longPathAware> + </asmv3:windowsSettings> + </asmv3:application> +</assembly> \ No newline at end of file
diff --git a/src/gn/file_writer.cc b/src/gn/file_writer.cc index 6c77821..7f0bb3f 100644 --- a/src/gn/file_writer.cc +++ b/src/gn/file_writer.cc
@@ -11,6 +11,7 @@ #if defined(OS_WIN) #include <windows.h> #include "base/strings/utf_string_conversions.h" +#include "util/sys_info.h" #else #include <fcntl.h> #include <unistd.h> @@ -30,14 +31,24 @@ // replacing the entire contents of the file) which lets us continue even if // another program has the file open for reading. See // http://crbug.com/468437 - file_path_ = base::UTF16ToUTF8(file_path.value()); + const std::u16string& path = file_path.value(); + + file_path_ = base::UTF16ToUTF8(path); file_ = base::win::ScopedHandle(::CreateFile( - reinterpret_cast<LPCWSTR>(file_path.value().c_str()), GENERIC_WRITE, + reinterpret_cast<LPCWSTR>(path.c_str()), GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, 0, NULL)); valid_ = file_.IsValid(); if (!valid_) { PLOG(ERROR) << "CreateFile failed for path " << file_path_; + + // Determine whether the path need long path support. + if (path.size() >= MAX_PATH && !IsLongPathsSupportEnabled()) { + LOG(ERROR) << "You might need to enable Long Path Support on Windows: " + << "https://learn.microsoft.com/en-us/windows/win32/fileio/" + "maximum-file-path-limitation?tabs=registry#enable-long-paths" + "-in-windows-10-version-1607-and-later"; + } } return valid_; }
diff --git a/src/gn/file_writer_unittest.cc b/src/gn/file_writer_unittest.cc index 49f533a..16f333d 100644 --- a/src/gn/file_writer_unittest.cc +++ b/src/gn/file_writer_unittest.cc
@@ -8,13 +8,15 @@ #include "base/files/scoped_temp_dir.h" #include "gn/filesystem_utils.h" +#include "util/build_config.h" +#include "util/sys_info.h" #include "util/test/test.h" TEST(FileWriter, SingleWrite) { base::ScopedTempDir temp_dir; ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); - std::string data = "foo"; + const std::string data = "foo"; base::FilePath file_path = temp_dir.GetPath().AppendASCII("foo.txt"); @@ -30,7 +32,7 @@ base::ScopedTempDir temp_dir; ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); - std::string data = "Hello World!"; + const std::string data = "Hello World!"; base::FilePath file_path = temp_dir.GetPath().AppendASCII("foo.txt"); @@ -42,3 +44,26 @@ EXPECT_TRUE(ContentsEqual(file_path, data)); } + +#if defined(OS_WIN) +TEST(FileWriter, LongPathWrite) { + if (!IsLongPathsSupportEnabled()) + return; + + base::ScopedTempDir temp_dir; + ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); + + const std::string data = "Hello World!"; + + base::FilePath file_path = temp_dir.GetPath().AppendASCII(std::string(255, 'A')); + + EXPECT_GE(file_path.value().size(), MAX_PATH); + + FileWriter writer; + EXPECT_TRUE(writer.Create(file_path)); + EXPECT_TRUE(writer.Write(data)); + EXPECT_TRUE(writer.Close()); + + EXPECT_TRUE(ContentsEqual(file_path, data)); +} +#endif \ No newline at end of file
diff --git a/src/util/sys_info.cc b/src/util/sys_info.cc index c8dc590..4cf71db 100644 --- a/src/util/sys_info.cc +++ b/src/util/sys_info.cc
@@ -14,8 +14,47 @@ #if defined(OS_WIN) #include <windows.h> +#include "base/win/registry.h" #endif +bool IsLongPathsSupportEnabled() { +#if defined(OS_WIN) + struct LongPathSupport { + LongPathSupport() { + // Probe ntdll.dll for RtlAreLongPathsEnabled, and call it if it exists. + HINSTANCE ntdll_lib = GetModuleHandleW(L"ntdll"); + if (ntdll_lib) { + using FunctionType = BOOLEAN(WINAPI*)(); + auto func_ptr = reinterpret_cast<FunctionType>( + GetProcAddress(ntdll_lib, "RtlAreLongPathsEnabled")); + if (func_ptr) { + supported = func_ptr(); + return; + } + } + + // If the ntdll approach failed, the registry approach is still reliable, + // because the manifest should've always be linked with gn.exe in Windows. + const char16_t key_name[] = uR"(SYSTEM\CurrentControlSet\Control\FileSystem)"; + const char16_t value_name[] = u"LongPathsEnabled"; + + base::win::RegKey key(HKEY_LOCAL_MACHINE, key_name, KEY_READ); + DWORD value; + if (key.ReadValueDW(value_name, &value) == ERROR_SUCCESS) { + supported = value == 1; + } + } + + bool supported = false; + }; + + static LongPathSupport s_long_paths; // constructed lazily + return s_long_paths.supported; +#else + return true; +#endif +} + std::string OperatingSystemArchitecture() { #if defined(OS_POSIX) struct utsname info;
diff --git a/src/util/sys_info.h b/src/util/sys_info.h index 68133e1..7a6924d 100644 --- a/src/util/sys_info.h +++ b/src/util/sys_info.h
@@ -7,6 +7,7 @@ #include <string> +bool IsLongPathsSupportEnabled(); std::string OperatingSystemArchitecture(); int NumberOfProcessors();