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();