diff --git a/.clang-format b/.clang-format new file mode 100644 index 00000000..40040656 --- /dev/null +++ b/.clang-format @@ -0,0 +1,12 @@ +# Copyright 2013 The Chromium Authors +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + BasedOnStyle: Chromium, + AlignTrailingComments: false, + BinPackArguments: false, + InsertBraces: true, + InsertNewlineAtEOF: true, + Standard: Cpp11, +} diff --git a/OWNERS b/OWNERS new file mode 100644 index 00000000..e4ca1291 --- /dev/null +++ b/OWNERS @@ -0,0 +1,12 @@ +# Copyright 2025 The Chromium Authors +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +set noparent + +jperaza@chromium.org +justincohen@chromium.org +lgrey@chromium.org +mark@chromium.org +pbos@chromium.org +wfh@chromium.org diff --git a/base/BUILD.gn b/base/BUILD.gn index 73f84fe6..ba6f6e57 100644 --- a/base/BUILD.gn +++ b/base/BUILD.gn @@ -63,7 +63,6 @@ static_library("base") { "strings/strcat_internal.h", "strings/string_number_conversions.cc", "strings/string_number_conversions.h", - "strings/string_piece.h", "strings/string_util.h", "strings/stringprintf.cc", "strings/stringprintf.h", @@ -155,9 +154,9 @@ static_library("base") { ] if (defined(is_fuchsia_tree) && is_fuchsia_tree) { - deps = [ "//zircon/system/ulib/syslog" ] + deps = [ "//sdk/lib/syslog/cpp" ] } else { - deps = [ "//third_party/fuchsia/sdk/$host_os-amd64/pkg/syslog" ] + deps = [ "//third_party/fuchsia/sdk/$host_os-amd64/pkg/syslog_cpp" ] } } diff --git a/base/apple/foundation_util.h b/base/apple/foundation_util.h index 1654c8a1..f0462942 100644 --- a/base/apple/foundation_util.h +++ b/base/apple/foundation_util.h @@ -70,11 +70,6 @@ CF_CAST_DECL(CGColor); CF_CAST_DECL(CTFont); CF_CAST_DECL(CTRun); -#if !BUILDFLAG(IS_IOS) -CF_CAST_DECL(SecACL); -CF_CAST_DECL(SecTrustedApplication); -#endif - #undef CF_CAST_DECL #if defined(__OBJC__) diff --git a/base/apple/foundation_util.mm b/base/apple/foundation_util.mm index 2c63ac99..ec591a6b 100644 --- a/base/apple/foundation_util.mm +++ b/base/apple/foundation_util.mm @@ -6,13 +6,6 @@ #include "base/check.h" -#if !BUILDFLAG(IS_IOS) -extern "C" { -CFTypeID SecACLGetTypeID(); -CFTypeID SecTrustedApplicationGetTypeID(); -} // extern "C" -#endif - namespace base { namespace apple { @@ -53,11 +46,6 @@ CF_CAST_DEFN(CTFont) CF_CAST_DEFN(CTRun) -#if !BUILDFLAG(IS_IOS) -CF_CAST_DEFN(SecACL) -CF_CAST_DEFN(SecTrustedApplication) -#endif - #undef CF_CAST_DEFN } // namespace apple diff --git a/base/apple/mach_logging.cc b/base/apple/mach_logging.cc index d5d51980..0083aca8 100644 --- a/base/apple/mach_logging.cc +++ b/base/apple/mach_logging.cc @@ -7,6 +7,7 @@ #include #include +#include "base/immediate_crash.h" #include "base/strings/stringprintf.h" #if !BUILDFLAG(IS_IOS) @@ -37,10 +38,20 @@ MachLogMessage::MachLogMessage(const char* function, : LogMessage(function, file_path, line, severity), mach_err_(mach_err) {} MachLogMessage::~MachLogMessage() { + AppendError(); +} + +void MachLogMessage::AppendError() { stream() << ": " << mach_error_string(mach_err_) << FormatMachErrorNumber(mach_err_); } +MachLogMessageFatal::~MachLogMessageFatal() { + AppendError(); + Flush(); + base::ImmediateCrash(); +} + #if !BUILDFLAG(IS_IOS) BootstrapLogMessage::BootstrapLogMessage(const char* function, @@ -52,6 +63,10 @@ BootstrapLogMessage::BootstrapLogMessage(const char* function, bootstrap_err_(bootstrap_err) {} BootstrapLogMessage::~BootstrapLogMessage() { + AppendError(); +} + +void BootstrapLogMessage::AppendError() { stream() << ": " << bootstrap_strerror(bootstrap_err_); switch (bootstrap_err_) { @@ -79,6 +94,12 @@ BootstrapLogMessage::~BootstrapLogMessage() { } } +BootstrapLogMessageFatal::~BootstrapLogMessageFatal() { + AppendError(); + Flush(); + base::ImmediateCrash(); +} + #endif // !BUILDFLAG(IS_IOS) } // namespace logging diff --git a/base/apple/mach_logging.h b/base/apple/mach_logging.h index 9ba89b49..5ca9216a 100644 --- a/base/apple/mach_logging.h +++ b/base/apple/mach_logging.h @@ -44,10 +44,19 @@ class MachLogMessage : public logging::LogMessage { ~MachLogMessage(); + protected: + void AppendError(); + private: mach_error_t mach_err_; }; +class MachLogMessageFatal final : public MachLogMessage { + public: + using MachLogMessage::MachLogMessage; + [[noreturn]] ~MachLogMessageFatal() override; +}; + } // namespace logging #define MACH_LOG_STREAM(severity, mach_err) \ @@ -108,10 +117,19 @@ class BootstrapLogMessage : public logging::LogMessage { ~BootstrapLogMessage(); + protected: + void AppendError(); + private: kern_return_t bootstrap_err_; }; +class BootstrapLogMessageFatal final : public BootstrapLogMessage { + public: + using BootstrapLogMessage::BootstrapLogMessage; + [[noreturn]] ~BootstrapLogMessageFatal() override; +}; + } // namespace logging #define BOOTSTRAP_LOG_STREAM(severity, bootstrap_err) \ diff --git a/base/atomicops.h b/base/atomicops.h index 00d7f755..5ecbac28 100644 --- a/base/atomicops.h +++ b/base/atomicops.h @@ -87,9 +87,6 @@ Atomic32 NoBarrier_AtomicExchange(volatile Atomic32* ptr, Atomic32 new_value); // *ptr with the increment applied. This routine implies no memory barriers. Atomic32 NoBarrier_AtomicIncrement(volatile Atomic32* ptr, Atomic32 increment); -Atomic32 Barrier_AtomicIncrement(volatile Atomic32* ptr, - Atomic32 increment); - // These following lower-level operations are typically useful only to people // implementing higher-level synchronization operations like spinlocks, // mutexes, and condition-variables. They combine CompareAndSwap(), a load, or @@ -122,7 +119,6 @@ Atomic64 NoBarrier_CompareAndSwap(volatile Atomic64* ptr, Atomic64 new_value); Atomic64 NoBarrier_AtomicExchange(volatile Atomic64* ptr, Atomic64 new_value); Atomic64 NoBarrier_AtomicIncrement(volatile Atomic64* ptr, Atomic64 increment); -Atomic64 Barrier_AtomicIncrement(volatile Atomic64* ptr, Atomic64 increment); Atomic64 Acquire_CompareAndSwap(volatile Atomic64* ptr, Atomic64 old_value, diff --git a/base/atomicops_internals_atomicword_compat.h b/base/atomicops_internals_atomicword_compat.h index c2fdad9a..b5d391b5 100644 --- a/base/atomicops_internals_atomicword_compat.h +++ b/base/atomicops_internals_atomicword_compat.h @@ -42,12 +42,6 @@ inline AtomicWord NoBarrier_AtomicIncrement(volatile AtomicWord* ptr, reinterpret_cast(ptr), increment); } -inline AtomicWord Barrier_AtomicIncrement(volatile AtomicWord* ptr, - AtomicWord increment) { - return Barrier_AtomicIncrement( - reinterpret_cast(ptr), increment); -} - inline AtomicWord Acquire_CompareAndSwap(volatile AtomicWord* ptr, AtomicWord old_value, AtomicWord new_value) { diff --git a/base/atomicops_internals_portable.h b/base/atomicops_internals_portable.h index 88e2d2d8..a3bda604 100644 --- a/base/atomicops_internals_portable.h +++ b/base/atomicops_internals_portable.h @@ -78,11 +78,6 @@ inline Atomic32 NoBarrier_AtomicIncrement(volatile Atomic32* ptr, ->fetch_add(increment, std::memory_order_relaxed); } -inline Atomic32 Barrier_AtomicIncrement(volatile Atomic32* ptr, - Atomic32 increment) { - return increment + ((AtomicLocation32)ptr)->fetch_add(increment); -} - inline Atomic32 Acquire_CompareAndSwap(volatile Atomic32* ptr, Atomic32 old_value, Atomic32 new_value) { @@ -161,11 +156,6 @@ inline Atomic64 NoBarrier_AtomicIncrement(volatile Atomic64* ptr, ->fetch_add(increment, std::memory_order_relaxed); } -inline Atomic64 Barrier_AtomicIncrement(volatile Atomic64* ptr, - Atomic64 increment) { - return increment + ((AtomicLocation64)ptr)->fetch_add(increment); -} - inline Atomic64 Acquire_CompareAndSwap(volatile Atomic64* ptr, Atomic64 old_value, Atomic64 new_value) { diff --git a/base/compiler_specific.h b/base/compiler_specific.h index f419c426..c3eb41ac 100644 --- a/base/compiler_specific.h +++ b/base/compiler_specific.h @@ -80,24 +80,6 @@ #define NOT_TAIL_CALLED #endif -// Specify memory alignment for structs, classes, etc. -// Use like: -// class ALIGNAS(16) MyClass { ... } -// ALIGNAS(16) int array[4]; -// -// In most places you can use the C++11 keyword "alignas", which is preferred. -// -// Historically, compilers had trouble mixing __attribute__((...)) syntax with -// alignas(...) syntax. However, at least Clang is very accepting nowadays. It -// may be that this macro can be removed entirely. -#if defined(__clang__) -#define ALIGNAS(byte_alignment) alignas(byte_alignment) -#elif defined(COMPILER_MSVC) -#define ALIGNAS(byte_alignment) __declspec(align(byte_alignment)) -#elif defined(COMPILER_GCC) && HAS_ATTRIBUTE(aligned) -#define ALIGNAS(byte_alignment) __attribute__((aligned(byte_alignment))) -#endif - // In case the compiler supports it NO_UNIQUE_ADDRESS evaluates to the C++20 // attribute [[no_unique_address]]. This allows annotating data members so that // they need not have an address distinct from all other non-static data members @@ -131,13 +113,6 @@ #define PRINTF_FORMAT(format_param, dots_param) #endif -// WPRINTF_FORMAT is the same, but for wide format strings. -// This doesn't appear to yet be implemented in any compiler. -// See http://gcc.gnu.org/bugzilla/show_bug.cgi?id=38308 . -#define WPRINTF_FORMAT(format_param, dots_param) -// If available, it would look like: -// __attribute__((format(wprintf, format_param, dots_param))) - // Sanitizers annotations. #if HAS_ATTRIBUTE(no_sanitize) #define NO_SANITIZE(what) __attribute__((no_sanitize(what))) @@ -205,32 +180,6 @@ #define DISABLE_CFI_DLSYM #endif -// Macro useful for writing cross-platform function pointers. -#if !defined(CDECL) -#if BUILDFLAG(IS_WIN) -#define CDECL __cdecl -#else // BUILDFLAG(IS_WIN) -#define CDECL -#endif // BUILDFLAG(IS_WIN) -#endif // !defined(CDECL) - -// Macro for hinting that an expression is likely to be false. -#if !defined(UNLIKELY) -#if defined(COMPILER_GCC) || defined(__clang__) -#define UNLIKELY(x) __builtin_expect(!!(x), 0) -#else -#define UNLIKELY(x) (x) -#endif // defined(COMPILER_GCC) -#endif // !defined(UNLIKELY) - -#if !defined(LIKELY) -#if defined(COMPILER_GCC) || defined(__clang__) -#define LIKELY(x) __builtin_expect(!!(x), 1) -#else -#define LIKELY(x) (x) -#endif // defined(COMPILER_GCC) -#endif // !defined(LIKELY) - // Compiler feature-detection. // clang.llvm.org/docs/LanguageExtensions.html#has-feature-and-has-extension #if defined(__has_feature) @@ -248,24 +197,6 @@ #define PRETTY_FUNCTION __func__ #endif -#if !defined(CPU_ARM_NEON) -#if defined(__arm__) -#if !defined(__ARMEB__) && !defined(__ARM_EABI__) && !defined(__EABI__) && \ - !defined(__VFP_FP__) && !defined(_WIN32_WCE) && !defined(ANDROID) -#error Chromium does not support middle endian architecture -#endif -#if defined(__ARM_NEON__) -#define CPU_ARM_NEON 1 -#endif -#endif // defined(__arm__) -#endif // !defined(CPU_ARM_NEON) - -#if !defined(HAVE_MIPS_MSA_INTRINSICS) -#if defined(__mips_msa) && defined(__mips_isa_rev) && (__mips_isa_rev >= 5) -#define HAVE_MIPS_MSA_INTRINSICS 1 -#endif -#endif - #if defined(__clang__) && HAS_ATTRIBUTE(uninitialized) // Attribute "uninitialized" disables -ftrivial-auto-var-init=pattern for // the specified variable. diff --git a/base/fuchsia/fuchsia_logging.cc b/base/fuchsia/fuchsia_logging.cc index 12dca721..10d881b2 100644 --- a/base/fuchsia/fuchsia_logging.cc +++ b/base/fuchsia/fuchsia_logging.cc @@ -8,6 +8,8 @@ #include +#include "base/immediate_crash.h" + namespace logging { ZxLogMessage::ZxLogMessage(const char* function, @@ -18,10 +20,20 @@ ZxLogMessage::ZxLogMessage(const char* function, : LogMessage(function, file_path, line, severity), zx_err_(zx_err) {} ZxLogMessage::~ZxLogMessage() { + AppendError(); +} + +void ZxLogMessage::AppendError() { // zx_status_t error values are negative, so log the numeric version as // decimal rather than hex. This is also useful to match zircon/errors.h for // grepping. stream() << ": " << zx_status_get_string(zx_err_) << " (" << zx_err_ << ")"; } +ZxLogMessageFatal::~ZxLogMessageFatal() { + AppendError(); + Flush(); + base::ImmediateCrash(); +} + } // namespace logging diff --git a/base/fuchsia/fuchsia_logging.h b/base/fuchsia/fuchsia_logging.h index 3f70cac8..0f1e95df 100644 --- a/base/fuchsia/fuchsia_logging.h +++ b/base/fuchsia/fuchsia_logging.h @@ -28,10 +28,19 @@ class ZxLogMessage : public logging::LogMessage { ~ZxLogMessage(); + protected: + void AppendError(); + private: zx_status_t zx_err_; }; +class ZxLogMessageFatal final : public ZxLogMessage { + public: + using ZxLogMessage::ZxLogMessage; + [[noreturn]] ~ZxLogMessageFatal() override; +}; + } // namespace logging #define ZX_LOG_STREAM(severity, zx_err) \ diff --git a/base/logging.cc b/base/logging.cc index 959b64fa..7a943d4a 100644 --- a/base/logging.cc +++ b/base/logging.cc @@ -19,29 +19,9 @@ #endif // BUILDFLAG(IS_POSIX) #if BUILDFLAG(IS_APPLE) -// In macOS 10.12 and iOS 10.0 and later ASL (Apple System Log) was deprecated -// in favor of OS_LOG (Unified Logging). -#include -#if BUILDFLAG(IS_IOS) -#if !defined(__IPHONE_10_0) || __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_10_0 -#define USE_ASL -#endif -#else // !BUILDFLAG(IS_IOS) -#if !defined(MAC_OS_X_VERSION_10_12) || \ - MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_12 -#define USE_ASL -#endif -#endif // BUILDFLAG(IS_IOS) - -#if defined(USE_ASL) -#include -#else -#include -#endif // USE_ASL - #include +#include #include - #elif BUILDFLAG(IS_LINUX) #include #include @@ -50,7 +30,7 @@ #elif BUILDFLAG(IS_ANDROID) #include #elif BUILDFLAG(IS_FUCHSIA) -#include +#include #endif #include "base/check_op.h" @@ -110,10 +90,10 @@ std::string SystemErrorCodeToString(unsigned long error_code) { if (len >= 1 && msgbuf[len - 1] == ' ') { msgbuf[len - 1] = '\0'; } - return base::StringPrintf("%s (%u)", + return base::StringPrintf("%s (%lu)", base::WideToUTF8(msgbuf).c_str(), error_code); } - return base::StringPrintf("Error %u while retrieving error %u", + return base::StringPrintf("Error %lu while retrieving error %lu", GetLastError(), error_code); } @@ -210,76 +190,6 @@ void LogMessage::Flush() { } } -#if defined(USE_ASL) - // Use ASL when this might run on pre-10.12 systems. Unified Logging - // (os_log) was introduced in 10.12. - - const class ASLClient { - public: - explicit ASLClient(const char* asl_facility) - : client_(asl_open(nullptr, asl_facility, ASL_OPT_NO_DELAY)) {} - - ASLClient(const ASLClient&) = delete; - ASLClient& operator=(const ASLClient&) = delete; - - ~ASLClient() { asl_close(client_); } - - aslclient get() const { return client_; } - - private: - aslclient client_; - } asl_client(main_bundle_id ? main_bundle_id : "com.apple.console"); - - const class ASLMessage { - public: - ASLMessage() : message_(asl_new(ASL_TYPE_MSG)) {} - - ASLMessage(const ASLMessage&) = delete; - ASLMessage& operator=(const ASLMessage&) = delete; - - ~ASLMessage() { asl_free(message_); } - - aslmsg get() const { return message_; } - - private: - aslmsg message_; - } asl_message; - - // By default, messages are only readable by the admin group. Explicitly - // make them readable by the user generating the messages. - char euid_string[12]; - snprintf(euid_string, std::size(euid_string), "%d", geteuid()); - asl_set(asl_message.get(), ASL_KEY_READ_UID, euid_string); - - // Map Chrome log severities to ASL log levels. - const char* const asl_level_string = [](LogSeverity severity) { -#define ASL_LEVEL_STR(level) ASL_LEVEL_STR_X(level) -#define ASL_LEVEL_STR_X(level) #level - switch (severity) { - case LOG_INFO: - return ASL_LEVEL_STR(ASL_LEVEL_INFO); - case LOG_WARNING: - return ASL_LEVEL_STR(ASL_LEVEL_WARNING); - case LOG_ERROR: - return ASL_LEVEL_STR(ASL_LEVEL_ERR); - case LOG_FATAL: - return ASL_LEVEL_STR(ASL_LEVEL_CRIT); - default: - return severity < 0 ? ASL_LEVEL_STR(ASL_LEVEL_DEBUG) - : ASL_LEVEL_STR(ASL_LEVEL_NOTICE); - } -#undef ASL_LEVEL_STR -#undef ASL_LEVEL_STR_X - }(severity_); - asl_set(asl_message.get(), ASL_KEY_LEVEL, asl_level_string); - - asl_set(asl_message.get(), ASL_KEY_MSG, str_newline.c_str()); - - asl_send(asl_client.get(), asl_message.get()); -#else - // Use Unified Logging (os_log) when this will only run on 10.12 and - // later. ASL is deprecated in 10.12. - const class OSLog { public: explicit OSLog(const char* subsystem) @@ -318,7 +228,6 @@ void LogMessage::Flush() { os_log_with_type( log.get(), os_log_type, "%{public}s", str_newline.c_str()); -#endif } #elif BUILDFLAG(IS_WIN) OutputDebugString(base::UTF8ToWide(str_newline).c_str()); @@ -342,36 +251,34 @@ void LogMessage::Flush() { // The Android system may truncate the string if it's too long. __android_log_write(priority, "chromium", str_newline.c_str()); #elif BUILDFLAG(IS_FUCHSIA) - fx_log_severity_t fx_severity; + fuchsia_logging::LogSeverity fx_severity; switch (severity_) { case LOG_INFO: - fx_severity = FX_LOG_INFO; + fx_severity = fuchsia_logging::LogSeverity::Info; break; case LOG_WARNING: - fx_severity = FX_LOG_WARNING; + fx_severity = fuchsia_logging::LogSeverity::Warn; break; case LOG_ERROR: - fx_severity = FX_LOG_ERROR; + fx_severity = fuchsia_logging::LogSeverity::Error; break; case LOG_FATAL: - fx_severity = FX_LOG_FATAL; + fx_severity = fuchsia_logging::LogSeverity::Fatal; break; default: - fx_severity = FX_LOG_INFO; + fx_severity = fuchsia_logging::LogSeverity::Info; break; } - // Temporarily remove the trailing newline from |str_newline|'s C-string - // representation, since fx_logger will add a newline of its own. - str_newline.pop_back(); + // Fuchsia's logger doesn't want the trailing newline. + std::string_view message(str_newline); + message.remove_suffix(1); + message.remove_prefix(message_start_); // Ideally the tag would be the same as the caller, but this is not // supported right now. - fx_logger_log_with_source(fx_log_get_logger(), - fx_severity, - /*tag=*/nullptr, - file_path_, - line_, - str_newline.c_str() + message_start_); - str_newline.push_back('\n'); + fuchsia_logging::LogMessage( + fx_severity, file_path_, line_, nullptr, nullptr) + .stream() + << message; #endif // BUILDFLAG(IS_*) } @@ -476,6 +383,22 @@ void LogMessage::Init(const char* function) { message_start_ = stream_.str().size(); } +// We intentionally don't return from these destructors. Disable MSVC's warning +// about the destructor never returning as we do so intentionally here. +#if defined(_MSC_VER) && !defined(__clang__) +#pragma warning(push) +#pragma warning(disable : 4722) +#endif + +LogMessageFatal::~LogMessageFatal() { + Flush(); + base::ImmediateCrash(); +} + +#if defined(_MSC_VER) && !defined(__clang__) +#pragma warning(pop) +#endif + #if BUILDFLAG(IS_WIN) unsigned long GetLastSystemErrorCode() { @@ -491,9 +414,30 @@ Win32ErrorLogMessage::Win32ErrorLogMessage(const char* function, } Win32ErrorLogMessage::~Win32ErrorLogMessage() { + AppendError(); +} + +void Win32ErrorLogMessage::AppendError() { stream() << ": " << SystemErrorCodeToString(err_); } +// We intentionally don't return from these destructors. Disable MSVC's warning +// about the destructor never returning as we do so intentionally here. +#if defined(_MSC_VER) && !defined(__clang__) +#pragma warning(push) +#pragma warning(disable : 4722) +#endif + +Win32ErrorLogMessageFatal::~Win32ErrorLogMessageFatal() { + AppendError(); + Flush(); + base::ImmediateCrash(); +} + +#if defined(_MSC_VER) && !defined(__clang__) +#pragma warning(pop) +#endif + #elif BUILDFLAG(IS_POSIX) ErrnoLogMessage::ErrnoLogMessage(const char* function, @@ -506,6 +450,10 @@ ErrnoLogMessage::ErrnoLogMessage(const char* function, } ErrnoLogMessage::~ErrnoLogMessage() { + AppendError(); +} + +void ErrnoLogMessage::AppendError() { stream() << ": " << base::safe_strerror(err_) << " (" @@ -513,6 +461,12 @@ ErrnoLogMessage::~ErrnoLogMessage() { << ")"; } +ErrnoLogMessageFatal::~ErrnoLogMessageFatal() { + AppendError(); + Flush(); + base::ImmediateCrash(); +} + #endif // BUILDFLAG(IS_POSIX) } // namespace logging diff --git a/base/logging.h b/base/logging.h index 30fb2d69..244d6a99 100644 --- a/base/logging.h +++ b/base/logging.h @@ -123,11 +123,10 @@ class LogMessage { LogSeverity severity_; }; -class LogMessageVoidify { +class LogMessageFatal final : public LogMessage { public: - LogMessageVoidify() {} - - void operator&(const std::ostream&) const {} + using LogMessage::LogMessage; + [[noreturn]] ~LogMessageFatal() override; }; #if BUILDFLAG(IS_WIN) @@ -144,9 +143,19 @@ class Win32ErrorLogMessage : public LogMessage { ~Win32ErrorLogMessage(); + protected: + void AppendError(); + private: unsigned long err_; }; + +class Win32ErrorLogMessageFatal final : public Win32ErrorLogMessage { + public: + using Win32ErrorLogMessage::Win32ErrorLogMessage; + [[noreturn]] ~Win32ErrorLogMessageFatal() override; +}; + #elif BUILDFLAG(IS_POSIX) class ErrnoLogMessage : public LogMessage { public: @@ -161,9 +170,18 @@ class ErrnoLogMessage : public LogMessage { ~ErrnoLogMessage(); + protected: + void AppendError(); + private: int err_; }; + +class ErrnoLogMessageFatal final : public ErrnoLogMessage { + public: + using ErrnoLogMessage::ErrnoLogMessage; + [[noreturn]] ~ErrnoLogMessageFatal() override; +}; #endif } // namespace logging @@ -187,8 +205,11 @@ class ErrnoLogMessage : public LogMessage { logging::ClassName(FUNCTION_SIGNATURE, __FILE__, __LINE__, \ logging::LOG_ERROR_REPORT, ## __VA_ARGS__) #define COMPACT_GOOGLE_LOG_EX_FATAL(ClassName, ...) \ - logging::ClassName(FUNCTION_SIGNATURE, __FILE__, __LINE__, \ - logging::LOG_FATAL, ## __VA_ARGS__) + logging::ClassName##Fatal(FUNCTION_SIGNATURE, \ + __FILE__, \ + __LINE__, \ + logging::LOG_FATAL, \ + ##__VA_ARGS__) #define COMPACT_GOOGLE_LOG_EX_DFATAL(ClassName, ...) \ logging::ClassName(FUNCTION_SIGNATURE, __FILE__, __LINE__, \ logging::LOG_DFATAL, ## __VA_ARGS__) @@ -228,10 +249,19 @@ const LogSeverity LOG_0 = LOG_ERROR; #endif // BUILDFLAG(IS_WIN) #define LAZY_STREAM(stream, condition) \ - !(condition) ? (void) 0 : ::logging::LogMessageVoidify() & (stream) - -#define LOG_IS_ON(severity) \ - ((::logging::LOG_ ## severity) >= ::logging::GetMinLogLevel()) + switch (0) \ + case 0: \ + default: \ + if (!(condition)) \ + ; \ + else \ + (stream) + +// FATAL is always enabled and required to be resolved in compile time for +// LOG(FATAL) to be properly understood as [[noreturn]]. +#define LOG_IS_ON(severity) \ + ((::logging::LOG_##severity) == ::logging::LOG_FATAL || \ + (::logging::LOG_##severity) >= ::logging::GetMinLogLevel()) #define VLOG_IS_ON(verbose_level) \ ((verbose_level) <= ::logging::GetVlogLevel(__FILE__)) diff --git a/base/metrics/persistent_histogram_allocator.h b/base/metrics/persistent_histogram_allocator.h index b67a1362..fc34907d 100644 --- a/base/metrics/persistent_histogram_allocator.h +++ b/base/metrics/persistent_histogram_allocator.h @@ -8,8 +8,9 @@ #include #include +#include + #include "base/files/file_path.h" -#include "base/strings/string_piece.h" // This file is a non-functional stub of the Chromium base interface to allow // Crashpad to set up and tear down histogram storage when built against @@ -25,11 +26,11 @@ class GlobalHistogramAllocator { static bool CreateWithActiveFileInDir(const base::FilePath&, size_t, uint64_t, - base::StringPiece sp) { + std::string_view sp) { return false; } - void CreateTrackingHistograms(base::StringPiece) {} + void CreateTrackingHistograms(std::string_view) {} void DeletePersistentLocation() {} static GlobalHistogramAllocator* Get() { return nullptr; } diff --git a/base/notreached.h b/base/notreached.h index 49cf8626..9c40c1f7 100644 --- a/base/notreached.h +++ b/base/notreached.h @@ -6,17 +6,8 @@ #define MINI_CHROMIUM_BASE_NOTREACHED_H_ #include "base/check.h" +#include "base/logging.h" -// TODO(crbug.com/40580068): Redefine NOTREACHED() to be [[noreturn]] once -// Crashpad and Chromium have migrated off of the non-noreturn version. This is -// easiest done by defining it as std::abort() as Crashpad currently doesn't -// stream arguments to it. For a more complete implementation we should use -// LOG(FATAL) but that is currently not annotated as [[noreturn]] because -// ~LogMessage is not. See TODO in base/logging.h -#define NOTREACHED() DCHECK(false) - -// TODO(crbug.com/40580068): Remove this once the NotReachedIsFatal experiment -// has been rolled out in Chromium. -#define NOTREACHED_IN_MIGRATION() DCHECK(false) +#define NOTREACHED() LOG(FATAL) << "NOTREACHED hit. " #endif // MINI_CHROMIUM_BASE_NOTREACHED_H_ diff --git a/base/numerics/basic_ops_impl.h b/base/numerics/basic_ops_impl.h index cde28389..ff0b6c99 100644 --- a/base/numerics/basic_ops_impl.h +++ b/base/numerics/basic_ops_impl.h @@ -10,7 +10,7 @@ #include #include -namespace base::numerics::internal { +namespace base::internal { #if defined(__GNUC__) || defined(__clang__) inline constexpr bool cxx17_is_constant_evaluated() noexcept { @@ -152,6 +152,6 @@ inline constexpr std::array ToLittleEndian(T val) { return bytes; } -} // namespace base::numerics::internal +} // namespace base::internal #endif // MINI_CHROMIUM_BASE_NUMERICS_BASIC_OPS_IMPL_H_ diff --git a/base/numerics/byte_conversions.h b/base/numerics/byte_conversions.h index 11f650b6..acd6b691 100644 --- a/base/numerics/byte_conversions.h +++ b/base/numerics/byte_conversions.h @@ -16,13 +16,13 @@ // Chromium only builds and runs on Little Endian machines. static_assert(ARCH_CPU_LITTLE_ENDIAN); -namespace base::numerics { +namespace base { // Returns a value with all bytes in |x| swapped, i.e. reverses the endianness. // TODO(pkasting): Once C++23 is available, replace with std::byteswap. template , int> = 0> inline constexpr T ByteSwap(T value) { - return numerics::internal::SwapBytes(value); + return internal::SwapBytes(value); } // Returns a uint8_t with the value in `bytes` interpreted as the native endian @@ -451,6 +451,6 @@ inline constexpr std::array DoubleToBigEndian(double val) { return internal::ToLittleEndian(ByteSwap(BitCast(val))); } -} // namespace base::numerics +} // namespace base #endif // MINI_CHROMIUM_BASE_NUMERICS_BYTE_CONVERSIONS_H_ diff --git a/base/numerics/safe_conversions_impl.h b/base/numerics/safe_conversions_impl.h index 36a6be94..53292b56 100644 --- a/base/numerics/safe_conversions_impl.h +++ b/base/numerics/safe_conversions_impl.h @@ -72,8 +72,9 @@ constexpr typename std::make_signed::type ConditionalNegate( static_assert(std::is_integral::value, "Type must be integral"); using SignedT = typename std::make_signed::type; using UnsignedT = typename std::make_unsigned::type; - return static_cast( - (static_cast(x) ^ -SignedT(is_negative)) + is_negative); + return static_cast((static_cast(x) ^ + static_cast(-SignedT(is_negative))) + + is_negative); } // This performs a safe, absolute value via unsigned overflow. diff --git a/base/strings/pattern.cc b/base/strings/pattern.cc index cc747aab..12f11aeb 100644 --- a/base/strings/pattern.cc +++ b/base/strings/pattern.cc @@ -4,6 +4,8 @@ #include "base/strings/pattern.h" +#include + #include "base/third_party/icu/icu_utf.h" namespace base { @@ -142,12 +144,12 @@ struct NextCharUTF16 { } // namespace -bool MatchPattern(StringPiece eval, StringPiece pattern) { +bool MatchPattern(std::string_view eval, std::string_view pattern) { return MatchPatternT(eval.data(), eval.data() + eval.size(), pattern.data(), pattern.data() + pattern.size(), NextCharUTF8()); } -bool MatchPattern(StringPiece16 eval, StringPiece16 pattern) { +bool MatchPattern(std::u16string_view eval, std::u16string_view pattern) { return MatchPatternT(eval.data(), eval.data() + eval.size(), pattern.data(), pattern.data() + pattern.size(), NextCharUTF16()); } diff --git a/base/strings/pattern.h b/base/strings/pattern.h index 20c13c76..45cf76b7 100644 --- a/base/strings/pattern.h +++ b/base/strings/pattern.h @@ -5,7 +5,7 @@ #ifndef BASE_STRINGS_PATTERN_H_ #define BASE_STRINGS_PATTERN_H_ -#include "base/strings/string_piece.h" +#include namespace base { @@ -14,8 +14,8 @@ namespace base { // // The backslash character (\) is an escape character for * and ?. // ? matches 0 or 1 character, while * matches 0 or more characters. -bool MatchPattern(StringPiece string, StringPiece pattern); -bool MatchPattern(StringPiece16 string, StringPiece16 pattern); +bool MatchPattern(std::string_view string, std::string_view pattern); +bool MatchPattern(std::u16string_view string, std::u16string_view pattern); } // namespace base diff --git a/base/strings/strcat.cc b/base/strings/strcat.cc index f322fc33..4ce6dddb 100644 --- a/base/strings/strcat.cc +++ b/base/strings/strcat.cc @@ -5,12 +5,13 @@ #include "base/strings/strcat.h" #include +#include #include "base/strings/strcat_internal.h" namespace base { -std::string StrCat(span pieces) { +std::string StrCat(span pieces) { return internal::StrCatT(pieces); } diff --git a/base/strings/strcat.h b/base/strings/strcat.h index d26c2fc0..330656a5 100644 --- a/base/strings/strcat.h +++ b/base/strings/strcat.h @@ -6,9 +6,9 @@ #define BASE_STRINGS_STRCAT_H_ #include +#include #include "base/containers/span.h" -#include "base/strings/string_piece.h" #include "build/build_config.h" #if BUILDFLAG(IS_WIN) @@ -32,7 +32,7 @@ namespace base { // StrCat can see all arguments at once, so it can allocate one return buffer // of exactly the right size and copy once, as opposed to a sequence of // operator+ which generates a series of temporary strings, copying as it goes. -// And by using StringPiece arguments, StrCat can avoid creating temporary +// And by using std::string_view arguments, StrCat can avoid creating temporary // string objects for char* constants. // // ALTERNATIVES @@ -49,15 +49,15 @@ namespace base { // and the call sites look nice. // // As-written Abseil's helper class for numbers generates slightly more code -// than the raw StringPiece version. We can de-inline the helper class' -// constructors which will cause the StringPiece constructors to be de-inlined -// for this call and generate slightly less code. This is something we can -// explore more in the future. +// than the raw std::string_view version. We can de-inline the helper class' +// constructors which will cause the std::string_view constructors to be +// de-inlined for this call and generate slightly less code. This is something +// we can explore more in the future. -[[nodiscard]] std::string StrCat(span pieces); +[[nodiscard]] std::string StrCat(span pieces); // Initializer list forwards to the array version. -inline std::string StrCat(std::initializer_list pieces) { +inline std::string StrCat(std::initializer_list pieces) { return StrCat(make_span(pieces)); } diff --git a/base/strings/string_number_conversions.cc b/base/strings/string_number_conversions.cc index cda1eafe..f46f04b0 100644 --- a/base/strings/string_number_conversions.cc +++ b/base/strings/string_number_conversions.cc @@ -8,6 +8,7 @@ #include #include +#include namespace base { @@ -185,25 +186,27 @@ class BaseIteratorRangeToNumberTraits { static constexpr int kBase = BASE; }; -typedef BaseIteratorRangeToNumberTraits +typedef BaseIteratorRangeToNumberTraits IteratorRangeToIntTraits; -typedef BaseIteratorRangeToNumberTraits +typedef BaseIteratorRangeToNumberTraits IteratorRangeToUintTraits; -typedef BaseIteratorRangeToNumberTraits IteratorRangeToInt64Traits; -typedef BaseIteratorRangeToNumberTraits IteratorRangeToUint64Traits; -typedef BaseIteratorRangeToNumberTraits IteratorRangeToSizeTTraits; @@ -217,39 +220,39 @@ class BaseHexIteratorRangeToIntTraits } }; -typedef BaseHexIteratorRangeToIntTraits +typedef BaseHexIteratorRangeToIntTraits HexIteratorRangeToIntTraits; } // namespace -bool StringToInt(const StringPiece& input, int* output) { +bool StringToInt(std::string_view input, int* output) { return IteratorRangeToNumber::Invoke(input.begin(), input.end(), output); } -bool StringToUint(const StringPiece& input, unsigned int* output) { +bool StringToUint(std::string_view input, unsigned int* output) { return IteratorRangeToNumber::Invoke(input.begin(), input.end(), output); } -bool StringToInt64(const StringPiece& input, int64_t* output) { +bool StringToInt64(std::string_view input, int64_t* output) { return IteratorRangeToNumber::Invoke( input.begin(), input.end(), output); } -bool StringToUint64(const StringPiece& input, uint64_t* output) { +bool StringToUint64(std::string_view input, uint64_t* output) { return IteratorRangeToNumber::Invoke( input.begin(), input.end(), output); } -bool StringToSizeT(const StringPiece& input, size_t* output) { +bool StringToSizeT(std::string_view input, size_t* output) { return IteratorRangeToNumber::Invoke( input.begin(), input.end(), output); } -bool HexStringToInt(const StringPiece& input, int* output) { +bool HexStringToInt(std::string_view input, int* output) { return IteratorRangeToNumber::Invoke( input.begin(), input.end(), output); } diff --git a/base/strings/string_number_conversions.h b/base/strings/string_number_conversions.h index 3f781c00..989b7abd 100644 --- a/base/strings/string_number_conversions.h +++ b/base/strings/string_number_conversions.h @@ -8,19 +8,18 @@ #include #include +#include #include -#include "base/strings/string_piece.h" - namespace base { -bool StringToInt(const StringPiece& input, int* output); -bool StringToUint(const StringPiece& input, unsigned int* output); -bool StringToInt64(const StringPiece& input, int64_t* output); -bool StringToUint64(const StringPiece& input, uint64_t* output); -bool StringToSizeT(const StringPiece& input, size_t* output); +bool StringToInt(std::string_view input, int* output); +bool StringToUint(std::string_view input, unsigned int* output); +bool StringToInt64(std::string_view input, int64_t* output); +bool StringToUint64(std::string_view input, uint64_t* output); +bool StringToSizeT(std::string_view input, size_t* output); -bool HexStringToInt(const StringPiece& input, int* output); +bool HexStringToInt(std::string_view input, int* output); bool HexStringToBytes(const std::string& input, std::vector* output); } // namespace base diff --git a/base/strings/string_piece.h b/base/strings/string_piece.h deleted file mode 100644 index a6a448bc..00000000 --- a/base/strings/string_piece.h +++ /dev/null @@ -1,201 +0,0 @@ -// Copyright 2006-2008 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef MINI_CHROMIUM_BASE_STRINGS_STRING_PIECE_H_ -#define MINI_CHROMIUM_BASE_STRINGS_STRING_PIECE_H_ - -#include -#include -#include -#include - -namespace base { - -template -class BasicStringPiece { - public: - typedef typename StringType::traits_type traits_type; - typedef typename StringType::value_type value_type; - typedef typename StringType::size_type size_type; - typedef typename StringType::difference_type difference_type; - typedef const value_type& reference; - typedef const value_type& const_reference; - typedef const value_type* pointer; - typedef const value_type* const_pointer; - typedef const value_type* const_iterator; - typedef std::reverse_iterator const_reverse_iterator; - - static const size_type npos; - - BasicStringPiece() - : pointer_(NULL), - length_(0) { - } - - BasicStringPiece(const value_type* string) - : pointer_(string), - length_((string == NULL) ? 0 : traits_type::length(string)) { - } - - BasicStringPiece(const StringType& string) - : pointer_(string.data()), - length_(string.size()) { - } - - BasicStringPiece(const value_type* offset, size_type length) - : pointer_(offset), - length_(length) { - } - - BasicStringPiece(const typename StringType::const_iterator& begin, - const typename StringType::const_iterator& end) - : pointer_((end > begin) ? &(*begin) : NULL), - length_((end > begin) ? static_cast(end - begin) : 0) { - } - - value_type operator[](size_type index) const { return pointer_[index]; } - - const value_type* data() const { return pointer_; } - - const_iterator begin() const { return pointer_; } - const_iterator end() const { return pointer_ + length_; } - const_reverse_iterator rbegin() const { - return const_reverse_iterator(pointer_ + length_); - } - const_reverse_iterator rend() const { - return const_reverse_iterator(pointer_); - } - - bool empty() const { return length_ == 0; } - size_type size() const { return length_; } - size_type length() const { return length_; } - size_type max_size() const { return length_; } - size_type capacity() const { return length_; } - - static int wordmemcmp(const value_type* p, - const value_type* p2, - size_type N) { - return StringType::traits_type::compare(p, p2, N); - } - - void clear() { - pointer_ = NULL; - length_ = 0; - } - - int compare(const BasicStringPiece& that) const { - int result = traits_type::compare(pointer_, - that.pointer_, - std::min(length_, that.length_)); - if (result == 0) { - if (length_ < that.length_) { - result = -1; - } else if (length_ > that.length_) { - result = 1; - } - } - return result; - } - - BasicStringPiece substr(size_type position = 0, - size_type count = npos) const { - position = std::min(position, size()); - count = std::min(count, size() - position); - return BasicStringPiece(data() + position, count); - } - - size_type copy(value_type* dest, - size_type count, - size_type position = 0) const { - size_type ret = std::min(size() - position, count); - traits_type::copy(dest, data() + position, ret); - return ret; - } - - size_type find(const BasicStringPiece& str, size_type pos) const { - if (pos >= size()) { - return npos; - } - const_iterator result = std::search(begin() + pos, - end(), - str.begin(), - str.end()); - size_type xpos = static_cast(result - begin()); - return xpos + str.size() <= size() ? xpos : npos; - } - - size_type find(value_type c, size_type pos) const { - if (pos >= size()) { - return npos; - } - const_iterator result = std::find(begin() + pos, end(), c); - return result != end() ? static_cast(result - begin()) : npos; - } - - void set(const value_type* string) { - pointer_ = string; - length_ = string ? traits_type::length(string) : 0; - } - - StringType as_string() const { - return empty() ? StringType() : StringType(data(), size()); - } - - private: - const value_type* pointer_; - size_type length_; -}; - -template -const typename BasicStringPiece::size_type - BasicStringPiece::npos = StringType::npos; - -template -std::ostream& operator<<(std::ostream& ostream, - const BasicStringPiece& string_piece) { - ostream.write(string_piece.data(), string_piece.size()); - return ostream; -} - -typedef BasicStringPiece StringPiece; -typedef BasicStringPiece StringPiece16; - -inline bool operator==(const StringPiece& x, const StringPiece& y) { - if (x.size() != y.size()) - return false; - - return StringPiece::wordmemcmp(x.data(), y.data(), x.size()) == 0; -} - -inline bool operator==(const StringPiece16& x, const StringPiece16& y) { - if (x.size() != y.size()) - return false; - - return StringPiece16::wordmemcmp(x.data(), y.data(), x.size()) == 0; -} - -// This is a custom hash function. We don't use the ones already defined for -// string and std::u16string directly because it would require the string -// constructors to be called, which we don't want. -#define HASH_STRING_PIECE(StringPieceType, string_piece) \ - std::size_t result = 0; \ - for (StringPieceType::const_iterator i = string_piece.begin(); \ - i != string_piece.end(); ++i) \ - result = (result * 131) + *i; \ - return result; - -struct StringPieceHash { - std::size_t operator()(const StringPiece& sp) const { - HASH_STRING_PIECE(StringPiece, sp); - } -}; -struct StringPiece16Hash { - std::size_t operator()(const StringPiece16& sp16) const { - HASH_STRING_PIECE(StringPiece16, sp16); - } -}; - -} // namespace base; - -#endif // MINI_CHROMIUM_BASE_STRINGS_STRING_PIECE_H_ diff --git a/base/strings/utf_string_conversions.cc b/base/strings/utf_string_conversions.cc index a1c243c7..8a7664c8 100644 --- a/base/strings/utf_string_conversions.cc +++ b/base/strings/utf_string_conversions.cc @@ -42,7 +42,7 @@ bool UTF8ToUTF16(const char* src, size_t src_len, std::u16string* output) { return ConvertUnicode(src, src_len, output); } -std::u16string UTF8ToUTF16(const StringPiece& utf8) { +std::u16string UTF8ToUTF16(std::string_view utf8) { std::u16string ret; UTF8ToUTF16(utf8.data(), utf8.length(), &ret); return ret; @@ -53,7 +53,7 @@ bool UTF16ToUTF8(const char16_t* src, size_t src_len, std::string* output) { return ConvertUnicode(src, src_len, output); } -std::string UTF16ToUTF8(const StringPiece16& utf16) { +std::string UTF16ToUTF8(std::u16string_view utf16) { std::string ret; UTF16ToUTF8(utf16.data(), utf16.length(), &ret); return ret; @@ -67,7 +67,7 @@ std::string WideToUTF8(std::wstring_view wide) { return ret; } -std::wstring UTF8ToWide(StringPiece utf8) { +std::wstring UTF8ToWide(std::string_view utf8) { std::u16string utf16 = UTF8ToUTF16(utf8); return std::wstring(reinterpret_cast(utf16.data()), utf16.size()); diff --git a/base/strings/utf_string_conversions.h b/base/strings/utf_string_conversions.h index 64c79c2b..7de2b819 100644 --- a/base/strings/utf_string_conversions.h +++ b/base/strings/utf_string_conversions.h @@ -8,19 +8,18 @@ #include #include -#include "base/strings/string_piece.h" #include "build/build_config.h" namespace base { bool UTF8ToUTF16(const char* src, size_t src_len, std::u16string* output); -std::u16string UTF8ToUTF16(const StringPiece& utf8); +std::u16string UTF8ToUTF16(std::string_view utf8); bool UTF16ToUTF8(const char16_t* src, size_t src_len, std::string* output); -std::string UTF16ToUTF8(const StringPiece16& utf16); +std::string UTF16ToUTF8(std::u16string_view utf16); #if defined(WCHAR_T_IS_16_BIT) std::string WideToUTF8(std::wstring_view wide); -std::wstring UTF8ToWide(StringPiece utf8); +std::wstring UTF8ToWide(std::string_view utf8); #endif // defined(WCHAR_T_IS_16_BIT) } // namespace diff --git a/base/template_util.h b/base/template_util.h index 7fce811e..5b6e0d44 100644 --- a/base/template_util.h +++ b/base/template_util.h @@ -5,6 +5,8 @@ #ifndef MINI_CHROMIUM_BASE_TEMPLATE_UTIL_H_ #define MINI_CHROMIUM_BASE_TEMPLATE_UTIL_H_ +#include + #include namespace base { diff --git a/base/threading/thread_local_storage.cc b/base/threading/thread_local_storage.cc index 8aa6c117..7ae9b255 100644 --- a/base/threading/thread_local_storage.cc +++ b/base/threading/thread_local_storage.cc @@ -160,10 +160,8 @@ void OnThreadExitInternal(void* value) { // the whole vector again. This is a pthread standard. need_to_scan_destructors = true; } - if (--remaining_attempts <= 0) { - NOTREACHED_IN_MIGRATION(); // Destructors might not have been called. - break; - } + // Destructors might not have been called. + CHECK_GT(--remaining_attempts, 0); } // Remove our stack allocated vector. diff --git a/build/build_config.h b/build/build_config.h index 62527454..d4921c12 100644 --- a/build/build_config.h +++ b/build/build_config.h @@ -13,9 +13,15 @@ #if defined(TARGET_OS_OSX) && TARGET_OS_OSX #define MINI_CHROMIUM_INTERNAL_BUILDFLAG_VALUE_IS_MAC() 1 #define MINI_CHROMIUM_INTERNAL_BUILDFLAG_VALUE_IS_IOS() 0 +#define MINI_CHROMIUM_INTERNAL_BUILDFLAG_VALUE_IS_IOS_TVOS() 0 #elif defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE #define MINI_CHROMIUM_INTERNAL_BUILDFLAG_VALUE_IS_MAC() 0 #define MINI_CHROMIUM_INTERNAL_BUILDFLAG_VALUE_IS_IOS() 1 +#if defined(TARGET_OS_TV) && TARGET_OS_TV +#define MINI_CHROMIUM_INTERNAL_BUILDFLAG_VALUE_IS_IOS_TVOS() 1 +#else +#define MINI_CHROMIUM_INTERNAL_BUILDFLAG_VALUE_IS_IOS_TVOS() 0 +#endif // TARGET_OS_TV #endif // TARGET_OS_* #define MINI_CHROMIUM_INTERNAL_BUILDFLAG_VALUE_IS_ANDROID() 0 #define MINI_CHROMIUM_INTERNAL_BUILDFLAG_VALUE_IS_LINUX() 0 @@ -27,6 +33,7 @@ #define MINI_CHROMIUM_INTERNAL_BUILDFLAG_VALUE_IS_APPLE() 0 #define MINI_CHROMIUM_INTERNAL_BUILDFLAG_VALUE_IS_MAC() 0 #define MINI_CHROMIUM_INTERNAL_BUILDFLAG_VALUE_IS_IOS() 0 +#define MINI_CHROMIUM_INTERNAL_BUILDFLAG_VALUE_IS_IOS_TVOS() 0 #define MINI_CHROMIUM_INTERNAL_BUILDFLAG_VALUE_IS_ANDROID() 1 #define MINI_CHROMIUM_INTERNAL_BUILDFLAG_VALUE_IS_LINUX() 0 #define MINI_CHROMIUM_INTERNAL_BUILDFLAG_VALUE_IS_CHROMEOS() 0 @@ -37,6 +44,7 @@ #define MINI_CHROMIUM_INTERNAL_BUILDFLAG_VALUE_IS_APPLE() 0 #define MINI_CHROMIUM_INTERNAL_BUILDFLAG_VALUE_IS_MAC() 0 #define MINI_CHROMIUM_INTERNAL_BUILDFLAG_VALUE_IS_IOS() 0 +#define MINI_CHROMIUM_INTERNAL_BUILDFLAG_VALUE_IS_IOS_TVOS() 0 #define MINI_CHROMIUM_INTERNAL_BUILDFLAG_VALUE_IS_ANDROID() 0 #if !defined(OS_CHROMEOS) #define MINI_CHROMIUM_INTERNAL_BUILDFLAG_VALUE_IS_LINUX() 1 @@ -52,6 +60,7 @@ #define MINI_CHROMIUM_INTERNAL_BUILDFLAG_VALUE_IS_APPLE() 0 #define MINI_CHROMIUM_INTERNAL_BUILDFLAG_VALUE_IS_MAC() 0 #define MINI_CHROMIUM_INTERNAL_BUILDFLAG_VALUE_IS_IOS() 0 +#define MINI_CHROMIUM_INTERNAL_BUILDFLAG_VALUE_IS_IOS_TVOS() 0 #define MINI_CHROMIUM_INTERNAL_BUILDFLAG_VALUE_IS_ANDROID() 0 #define MINI_CHROMIUM_INTERNAL_BUILDFLAG_VALUE_IS_LINUX() 0 #define MINI_CHROMIUM_INTERNAL_BUILDFLAG_VALUE_IS_CHROMEOS() 0 @@ -62,6 +71,7 @@ #define MINI_CHROMIUM_INTERNAL_BUILDFLAG_VALUE_IS_APPLE() 0 #define MINI_CHROMIUM_INTERNAL_BUILDFLAG_VALUE_IS_MAC() 0 #define MINI_CHROMIUM_INTERNAL_BUILDFLAG_VALUE_IS_IOS() 0 +#define MINI_CHROMIUM_INTERNAL_BUILDFLAG_VALUE_IS_IOS_TVOS() 0 #define MINI_CHROMIUM_INTERNAL_BUILDFLAG_VALUE_IS_ANDROID() 0 #define MINI_CHROMIUM_INTERNAL_BUILDFLAG_VALUE_IS_LINUX() 0 #define MINI_CHROMIUM_INTERNAL_BUILDFLAG_VALUE_IS_CHROMEOS() 0 @@ -141,4 +151,24 @@ #error Please add support for your compiler in build/build_config.h #endif +// Architecture-specific feature detection. + +#if !defined(CPU_ARM_NEON) +#if defined(__arm__) +#if !defined(__ARMEB__) && !defined(__ARM_EABI__) && !defined(__EABI__) && \ + !defined(__VFP_FP__) && !defined(_WIN32_WCE) && !defined(ANDROID) +#error Chromium does not support middle endian architecture +#endif +#if defined(__ARM_NEON__) +#define CPU_ARM_NEON 1 +#endif +#endif // defined(__arm__) +#endif // !defined(CPU_ARM_NEON) + +#if !defined(HAVE_MIPS_MSA_INTRINSICS) +#if defined(__mips_msa) && defined(__mips_isa_rev) && (__mips_isa_rev >= 5) +#define HAVE_MIPS_MSA_INTRINSICS 1 +#endif +#endif + #endif // MINI_CHROMIUM_BUILD_BUILD_CONFIG_H_ diff --git a/build/compiler.gni b/build/compiler.gni index ddaffc82..7e2b3ce6 100644 --- a/build/compiler.gni +++ b/build/compiler.gni @@ -4,4 +4,12 @@ import("platform.gni") -mini_chromium_is_clang = mini_chromium_is_posix || mini_chromium_is_fuchsia +if (mini_chromium_is_win) { + declare_args() { + mini_chromium_is_clang = mini_chromium_is_posix || + mini_chromium_is_fuchsia || mini_chromium_is_win + } +} else { + mini_chromium_is_clang = + mini_chromium_is_posix || mini_chromium_is_fuchsia || mini_chromium_is_win +} diff --git a/build/config/BUILD.gn b/build/config/BUILD.gn index 0802be8e..98533ee1 100644 --- a/build/config/BUILD.gn +++ b/build/config/BUILD.gn @@ -15,7 +15,7 @@ if (mini_chromium_is_mac) { # The minimum runtime macOS version that built products are expected to run # on. If empty, the toolchain will choose its own default, typically the # older of the SDK version and the build host’s OS version. - mac_deployment_target = "10.9" + mac_deployment_target = "12.0" } } else if (mini_chromium_is_ios) { import("../ios/ios_sdk.gni") @@ -44,6 +44,9 @@ if (mini_chromium_is_mac) { # win_sdk\bin\SetEnv.cmd inside this path will be used to configure the # Windows toolchain. win_toolchain_path = "" + + # Path to the Clang toolchain. + clang_path = "//third_party/windows/clang/" + host_os + "-amd64" } } @@ -108,19 +111,24 @@ config("release") { } } else if (mini_chromium_is_win) { cflags = [ - "/GL", # LTCG. "/O2", "/Ob2", # Both explicit and auto inlining. "/Oy-", # Disable omitting frame pointers, must be after /O2. "/Zc:inline", # Remove unreferenced COMDAT (faster links). - "/d2Zi+", # Improve debugging of optimized code. ] ldflags = [ "/OPT:ICF", "/OPT:REF", - "/LTCG", ] - arflags = [ "/LTCG" ] + arflags = [] + if (!mini_chromium_is_clang) { + cflags += [ + "/GL", # LTCG. + "/d2Zi+", # Improve debugging of optimized code. + ] + ldflags += [ "/LTCG" ] + arflags += [ "/LTCG" ] + } } } @@ -132,17 +140,8 @@ config("default") { if (mini_chromium_is_posix || mini_chromium_is_fuchsia) { cflags = [ "-Wall", - "-Wendif-labels", "-Werror", "-Wextra", - "-Wextra-semi", - "-Wheader-hygiene", - "-Wnewline-eof", - "-Wno-missing-field-initializers", - "-Wno-unused-parameter", - "-Wsign-compare", - "-Wstring-conversion", - "-Wvla", "-fno-exceptions", "-fno-rtti", "-fno-strict-aliasing", # See https://crbug.com/32204 @@ -225,11 +224,20 @@ config("default") { if (mini_chromium_is_ios) { if (ios_deployment_target != "") { - if (target_environment == "simulator") { - common_flags += - [ "-mios-simulator-version-min=" + ios_deployment_target ] - } else if (target_environment == "device") { - common_flags += [ "-mios-version-min=" + ios_deployment_target ] + if (target_platform == "iphoneos") { + if (target_environment == "simulator") { + common_flags += + [ "-mios-simulator-version-min=" + ios_deployment_target ] + } else if (target_environment == "device") { + common_flags += [ "-mios-version-min=" + ios_deployment_target ] + } + } else if (target_platform == "tvos") { + if (target_environment == "simulator") { + common_flags += + [ "-mtvos-simulator-version-min=" + ios_deployment_target ] + } else if (target_environment == "device") { + common_flags += [ "-mtvos-version-min=" + ios_deployment_target ] + } } } } @@ -247,18 +255,33 @@ config("default") { "/WX", "/Zi", "/bigobj", # Support larger number of sections in obj file. - "/wd4100", # Unreferenced formal parameter. - "/wd4127", # Conditional expression is constant. - "/wd4324", # Structure was padded due to alignment specifier. - "/wd4351", # New behavior: elements of array will be default initialized. - "/wd4577", # 'noexcept' used with no exception handling mode specified. "/wd4996", # 'X' was declared deprecated. ] - cflags_cc = [ - "/std:c++20", - "/Zc:__cplusplus", - ] + cflags_cc = [ "/std:c++20" ] + + if (mini_chromium_is_clang) { + if (current_cpu == "x86") { + cflags += [ "--target=i386-pc-windows" ] + } else if (current_cpu == "x64") { + cflags += [ "--target=x86_64-pc-windows" ] + } else if (current_cpu == "arm64") { + cflags += [ "--target=aarch64-pc-windows" ] + } else { + assert(false, "Unsupported architecture") + } + } else { + cflags += [ + "/wd4100", # Unreferenced formal parameter. + "/wd4127", # Conditional expression is constant. + "/wd4324", # Structure was padded due to alignment specifier. + "/wd4351", # elements of array will be default initialized. + "/wd4577", # 'noexcept' used with no exception handling mode specified. + ] + + # This is the default in clang, no need to pass it there. + cflags_cc += [ "/Zc:__cplusplus" ] + } ldflags += [ "/DEBUG" ] @@ -327,6 +350,34 @@ config("default") { ] } + if (mini_chromium_is_posix || mini_chromium_is_fuchsia || + mini_chromium_is_clang) { + cflags += [ + "-Wendif-labels", + "-Wextra-semi", + "-Wheader-hygiene", + "-Wnewline-eof", + "-Wno-missing-field-initializers", + "-Wno-unused-parameter", + "-Wsign-compare", + "-Wstring-conversion", + "-Wvla", + ] + + if (mini_chromium_is_win) { + # TODO: These should be cleaned up and enabled. + cflags += [ + "-Wno-cast-function-type-mismatch", # Currently 22 instances. + "-Wno-format", # Currently 3 instances. + "-Wno-microsoft-cast", # Currently 2 instances. + "-Wno-missing-field-initializers", # Currently 24 instances. + "-Wno-sign-compare", # Currently 6 instances. + "-Wno-unused-const-variable", # Currently 1 instance. + "-Wno-unused-function", # Currently 1 instance. + ] + } + } + if ((mini_chromium_is_posix && !mini_chromium_is_mac && !mini_chromium_is_ios) || mini_chromium_is_fuchsia) { cflags += [ "-fPIC" ] @@ -634,10 +685,17 @@ if (mini_chromium_is_win) { toolchain("msvc_toolchain_$target_name") { # @rsp files are not used for simplicity, and because mini_chromium and # Crashpad shouldn't require them in any configurations. - cc = "cl.exe" - cxx = "cl.exe" - ar = "lib.exe" - ld = "link.exe" + if (mini_chromium_is_clang) { + cc = rebase_path(clang_path, root_build_dir) + "/bin/clang-cl.exe" + cxx = rebase_path(clang_path, root_build_dir) + "/bin/clang-cl.exe" + ar = rebase_path(clang_path, root_build_dir) + "/bin/lld-link.exe /lib" + ld = rebase_path(clang_path, root_build_dir) + "/bin/lld-link.exe" + } else { + cc = "cl.exe" + cxx = "cl.exe" + ar = "lib.exe" + ld = "link.exe" + } lib_switch = "" lib_dir_switch = "/LIBPATH:" env = invoker.environment_file @@ -717,17 +775,43 @@ if (mini_chromium_is_win) { } tool("asm") { - if (invoker.current_cpu == "arm64") { - ml = "armasm64.exe" - command = "$python_path $helper_path asm-wrapper $env $ml {{include_dirs}} {{asmflags}} -o {{output}} {{source}}" + is_msvc_assembler = true + + if (mini_chromium_is_clang) { + if (invoker.current_cpu == "arm64") { + ml = "$cc --target=aarch64-pc-windows -o{{output}} /showIncludes" + is_msvc_assembler = false + depsformat = "msvc" + } else { + ml = rebase_path(clang_path, root_build_dir) + "/bin/llvm-ml.exe" + if (invoker.current_cpu == "x64") { + ml += " -m64" + } else { + ml += " -m32" + } + } } else { - if (invoker.current_cpu == "x86") { + if (invoker.current_cpu == "arm64") { + ml = "armasm64.exe" + } else if (invoker.current_cpu == "x64") { + ml = "ml64.exe" + } else { ml = "ml.exe" + } + } + + if (is_msvc_assembler) { + ml += " /nologo /Fo{{output}}" + + if (invoker.current_cpu == "arm64") { + command = "\"$python_path\" $helper_path asm-wrapper $env $ml {{include_dirs}} {{asmflags}} {{source}}" } else { - ml = "ml64.exe" + command = "\"$python_path\" $helper_path asm-wrapper $env $ml {{defines}} {{include_dirs}} {{asmflags}} /c {{source}}" } - command = "$python_path $helper_path asm-wrapper $env $ml {{defines}} {{include_dirs}} {{asmflags}} /c /Fo{{output}} {{source}}" + } else { + command = "ninja -t msvc -e $env -- $ml {{defines}} {{include_dirs}} {{asmflags}} {{source}}" } + description = "ASM {{output}}" outputs = [ "{{source_out_dir}}/{{label_name}}.{{source_name_part}}.obj" ] diff --git a/build/find_mac_sdk.py b/build/find_mac_sdk.py index 582c118a..6c8b0d3d 100755 --- a/build/find_mac_sdk.py +++ b/build/find_mac_sdk.py @@ -8,7 +8,6 @@ from __future__ import print_function import argparse -import distutils.version import os import re import subprocess @@ -16,149 +15,157 @@ import textwrap -def _AsVersion(string): - return distutils.version.StrictVersion(string) +def _AsVersion(version_str): + return tuple((int(s) for s in re.findall(r'(\d+)', version_str))) def _RunXCRun(args, sdk=None): - xcrun_args = ['xcrun'] - if sdk is not None: - xcrun_args.extend(['--sdk', sdk]) - xcrun_args.extend(args) - return subprocess.check_output(xcrun_args).decode('utf-8').rstrip() + xcrun_args = ['xcrun'] + if sdk is not None: + xcrun_args.extend(['--sdk', sdk]) + xcrun_args.extend(args) + return subprocess.check_output(xcrun_args).decode('utf-8').rstrip() def _SDKPath(sdk=None): - return _RunXCRun(['--show-sdk-path'], sdk) + return _RunXCRun(['--show-sdk-path'], sdk) def _SDKVersion(sdk=None): - return _AsVersion(_RunXCRun(['--show-sdk-version'], sdk)) + return _AsVersion(_RunXCRun(['--show-sdk-version'], sdk)) class DidNotMeetCriteria(Exception): - pass + pass def _FindPlatformSDKWithMinimumVersion(platform, minimum_sdk_version_str): - minimum_sdk_version = _AsVersion(minimum_sdk_version_str) - - # Try the SDKs that Xcode knows about. - xcodebuild_showsdks_subprocess = subprocess.Popen( - ['xcodebuild', '-showsdks'], - stdout=subprocess.PIPE, - stderr=open(os.devnull, 'w')) - xcodebuild_showsdks_output = ( - xcodebuild_showsdks_subprocess.communicate()[0].decode('utf-8')) - if xcodebuild_showsdks_subprocess.returncode == 0: - # Collect strings instead of version objects to preserve the precise - # format used to identify each SDK. - sdk_version_strs = [] - for line in xcodebuild_showsdks_output.splitlines(): - match = re.match('[ \t].+[ \t]-sdk ' + re.escape(platform) + '(.+)$', - line) - if match: - sdk_version_str = match.group(1) - if _AsVersion(sdk_version_str) >= minimum_sdk_version: - sdk_version_strs.append(sdk_version_str) - - if len(sdk_version_strs) == 0: - raise DidNotMeetCriteria({'minimum': minimum_sdk_version_str, - 'platform': platform}) - sdk_version_str = sorted(sdk_version_strs, key=_AsVersion)[0] - sdk_path = _SDKPath(platform + sdk_version_str) - sdk_version = _AsVersion(sdk_version_str) - else: - # Xcode may not be installed. If the command-line tools are installed, use - # the system’s default SDK if it meets the requirements. - sdk_path = _SDKPath() - sdk_version = _SDKVersion() - if sdk_version < minimum_sdk_version: - raise DidNotMeetCriteria({'minimum': minimum_sdk_version_str, - 'platform': platform, - 'sdk_path': sdk_path, - 'sdk_version': str(sdk_version)}) - - return (sdk_version, sdk_path) + minimum_sdk_version = _AsVersion(minimum_sdk_version_str) + + # Try the SDKs that Xcode knows about. + xcodebuild_showsdks_subprocess = subprocess.Popen( + ['xcodebuild', '-showsdks'], + stdout=subprocess.PIPE, + stderr=open(os.devnull, 'w')) + xcodebuild_showsdks_output = ( + xcodebuild_showsdks_subprocess.communicate()[0].decode('utf-8')) + if xcodebuild_showsdks_subprocess.returncode == 0: + # Collect strings instead of version objects to preserve the precise + # format used to identify each SDK. + sdk_version_strs = [] + for line in xcodebuild_showsdks_output.splitlines(): + match = re.match( + '[ \t].+[ \t]-sdk ' + re.escape(platform) + '(.+)$', line) + if match: + sdk_version_str = match.group(1) + if _AsVersion(sdk_version_str) >= minimum_sdk_version: + sdk_version_strs.append(sdk_version_str) + + if len(sdk_version_strs) == 0: + raise DidNotMeetCriteria({ + 'minimum': minimum_sdk_version_str, + 'platform': platform + }) + sdk_version_str = sorted(sdk_version_strs, key=_AsVersion)[0] + sdk_path = _SDKPath(platform + sdk_version_str) + sdk_version = _AsVersion(sdk_version_str) + else: + # Xcode may not be installed. If the command-line tools are installed, + # use the system’s default SDK if it meets the requirements. + sdk_path = _SDKPath() + sdk_version = _SDKVersion() + if sdk_version < minimum_sdk_version: + raise DidNotMeetCriteria({ + 'minimum': minimum_sdk_version_str, + 'platform': platform, + 'sdk_path': sdk_path, + 'sdk_version': str(sdk_version) + }) + + return (sdk_version, sdk_path) def main(args): - parser = argparse.ArgumentParser( - description='Find an appropriate platform SDK', - epilog='Two lines will be written to standard output: the version of the ' - 'selected SDK, and its path.') - parser.add_argument('--developer-dir', - help='path to Xcode or Command Line Tools') - parser.add_argument('--exact', help='an exact SDK version to find') - parser.add_argument('--minimum', help='the minimum SDK version to find') - parser.add_argument('--path', help='a known SDK path to validate') - parser.add_argument('--platform', - default='macosx', - help='the platform to target') - parsed = parser.parse_args(args) - - if parsed.developer_dir is not None: - os.environ['DEVELOPER_DIR'] = parsed.developer_dir - - if (os.environ.get('DEVELOPER_DIR') is None and - subprocess.call(['xcode-select', '--print-path'], - stdout=open(os.devnull, 'w'), - stderr=open(os.devnull, 'w')) != 0): - # This is friendlier than letting the first invocation of xcrun or - # xcodebuild show the UI prompting to install developer tools at an - # inopportune time. - hint = 'Install Xcode and run "sudo xcodebuild -license"' - if parsed.platform == 'macosx': - hint += ', or install Command Line Tools with "xcode-select --install"' - hint += ('. If necessary, run "sudo xcode-select --switch" to select an ' - 'active developer tools installation.') - hint = '\n'.join(textwrap.wrap(hint, 80)) - print(os.path.basename(sys.argv[0]) + - ': No developer tools found.\n' + + parser = argparse.ArgumentParser( + description='Find an appropriate platform SDK', + epilog='Two lines will be written to standard output: the version of ' + 'the selected SDK, and its path.') + parser.add_argument('--developer-dir', + help='path to Xcode or Command Line Tools') + parser.add_argument('--exact', help='an exact SDK version to find') + parser.add_argument('--minimum', help='the minimum SDK version to find') + parser.add_argument('--path', help='a known SDK path to validate') + parser.add_argument('--platform', + default='macosx', + help='the platform to target') + parsed = parser.parse_args(args) + + if parsed.developer_dir is not None: + os.environ['DEVELOPER_DIR'] = parsed.developer_dir + + if (os.environ.get('DEVELOPER_DIR') is None and + subprocess.call(['xcode-select', '--print-path'], + stdout=open(os.devnull, 'w'), + stderr=open(os.devnull, 'w')) != 0): + # This is friendlier than letting the first invocation of xcrun or + # xcodebuild show the UI prompting to install developer tools at an + # inopportune time. + hint = 'Install Xcode and run "sudo xcodebuild -license"' + if parsed.platform == 'macosx': + hint += ( + ', or install Command Line Tools with "xcode-select --install"') + hint += ( + '. If necessary, run "sudo xcode-select --switch" to select an ' + 'active developer tools installation.') + hint = '\n'.join(textwrap.wrap(hint, 80)) + print(os.path.basename(sys.argv[0]) + ': No developer tools found.\n' + hint, - file=sys.stderr) - return 1 - - if parsed.path is not None: - # _SDKVersion() doesn’t work with a relative pathname argument or one that’s - # a symbolic link. Such paths are suitable for other purposes, like “clang - # -isysroot”, so use an absolute non-symbolic link path for _SDKVersion(), - # but preserve the user’s path in sdk_path. - sdk_version = _SDKVersion(os.path.realpath(parsed.path)) - sdk_path = parsed.path - elif parsed.exact is None and parsed.minimum is None: - # Use the platform’s default SDK. - sdk_version = _SDKVersion(parsed.platform) - sdk_path = _SDKPath(parsed.platform) - elif parsed.exact is not None: - sdk_version = _SDKVersion(parsed.platform + parsed.exact) - sdk_path = _SDKPath(parsed.platform + parsed.exact) - else: - (sdk_version, - sdk_path) = _FindPlatformSDKWithMinimumVersion(parsed.platform, - parsed.minimum) - - # These checks may be redundant depending on how the SDK was chosen. - if ((parsed.exact is not None and sdk_version != _AsVersion(parsed.exact)) or - (parsed.minimum is not None and - sdk_version < _AsVersion(parsed.minimum))): - raise DidNotMeetCriteria({'developer_dir': parsed.developer_dir, - 'exact': parsed.exact, - 'minimum': parsed.minimum, - 'path': parsed.path, - 'platform': parsed.platform, - 'sdk_path': sdk_path, - 'sdk_version': str(sdk_version)}) - - # Nobody wants trailing slashes. This is true even if “/” is the SDK: it’s - # better to return an empty string, which will be interpreted as “no sysroot.” - sdk_path = sdk_path.rstrip(os.path.sep) - - print(sdk_version) - print(sdk_path) - - return 0 + file=sys.stderr) + return 1 + + if parsed.path is not None: + # _SDKVersion() doesn’t work with a relative pathname argument or one + # that’s a symbolic link. Such paths are suitable for other purposes, + # like “clang -isysroot”, so use an absolute non-symbolic link path for + # _SDKVersion(), but preserve the user’s path in sdk_path. + sdk_version = _SDKVersion(os.path.realpath(parsed.path)) + sdk_path = parsed.path + elif parsed.exact is None and parsed.minimum is None: + # Use the platform’s default SDK. + sdk_version = _SDKVersion(parsed.platform) + sdk_path = _SDKPath(parsed.platform) + elif parsed.exact is not None: + sdk_version = _SDKVersion(parsed.platform + parsed.exact) + sdk_path = _SDKPath(parsed.platform + parsed.exact) + else: + (sdk_version, + sdk_path) = _FindPlatformSDKWithMinimumVersion(parsed.platform, + parsed.minimum) + + # These checks may be redundant depending on how the SDK was chosen. + if ((parsed.exact is not None and sdk_version != _AsVersion(parsed.exact)) + or (parsed.minimum is not None and + sdk_version < _AsVersion(parsed.minimum))): + raise DidNotMeetCriteria({ + 'developer_dir': parsed.developer_dir, + 'exact': parsed.exact, + 'minimum': parsed.minimum, + 'path': parsed.path, + 'platform': parsed.platform, + 'sdk_path': sdk_path, + 'sdk_version': str(sdk_version) + }) + + # Nobody wants trailing slashes. This is true even if “/” is the SDK: it’s + # better to return an empty string, which will be interpreted as “no + # sysroot.” + sdk_path = sdk_path.rstrip(os.path.sep) + + print(sdk_version) + print(sdk_path) + + return 0 if __name__ == '__main__': - sys.exit(main(sys.argv[1:])) + sys.exit(main(sys.argv[1:])) diff --git a/build/ios/BuildInfo.plist b/build/ios/BuildInfo.plist index efd4e670..6b0ae1f0 100644 --- a/build/ios/BuildInfo.plist +++ b/build/ios/BuildInfo.plist @@ -30,6 +30,7 @@ 1 2 + 3 diff --git a/build/ios/codesign.py b/build/ios/codesign.py index 9b539c3b..ac05d06f 100644 --- a/build/ios/codesign.py +++ b/build/ios/codesign.py @@ -18,674 +18,709 @@ import tempfile if sys.version_info.major < 3: - basestring_compat = basestring + basestring_compat = basestring else: - basestring_compat = str + basestring_compat = str def GetProvisioningProfilesDir(): - """Returns the location of the installed mobile provisioning profiles. + """Returns the location of the installed mobile provisioning profiles. - Returns: - The path to the directory containing the installed mobile provisioning - profiles as a string. - """ - return os.path.join( - os.environ['HOME'], 'Library', 'MobileDevice', 'Provisioning Profiles') + Returns: + The path to the directory containing the installed mobile provisioning + profiles as a string. + """ + return os.path.join(os.environ['HOME'], 'Library', 'MobileDevice', + 'Provisioning Profiles') def ReadPlistFromString(plist_bytes): - """Parse property list from given |plist_bytes|. + """Parse property list from given |plist_bytes|. Args: - plist_bytes: contents of property list to load. Must be bytes in python 3. + plist_bytes: contents of property list to load. Must be bytes in python + 3. Returns: - The contents of property list as a python object. + The contents of property list as a python object. """ - if sys.version_info.major == 2: - return plistlib.readPlistFromString(plist_bytes) - else: - return plistlib.loads(plist_bytes) + if sys.version_info.major == 2: + return plistlib.readPlistFromString(plist_bytes) + else: + return plistlib.loads(plist_bytes) def LoadPlistFile(plist_path): - """Loads property list file at |plist_path|. - - Args: - plist_path: path to the property list file to load. - - Returns: - The content of the property list file as a python object. - """ - if sys.version_info.major == 2: - return plistlib.readPlistFromString( - subprocess.check_output( - ['xcrun', 'plutil', '-convert', 'xml1', '-o', '-', plist_path])) - else: - with open(plist_path, 'rb') as fp: - return plistlib.load(fp) - - -def CreateSymlink(value, location): - """Creates symlink with value at location if the target exists.""" - target = os.path.join(os.path.dirname(location), value) - if os.path.exists(location): - os.unlink(location) - os.symlink(value, location) - - -class Bundle(object): - """Wraps a bundle.""" - - def __init__(self, bundle_path, platform): - """Initializes the Bundle object with data from bundle Info.plist file.""" - self._path = bundle_path - self._kind = Bundle.Kind(platform, os.path.splitext(bundle_path)[-1]) - self._data = None - - def Load(self): - self._data = LoadPlistFile(self.info_plist_path) - - @staticmethod - def Kind(platform, extension): - if platform == 'iphonesimulator' or platform == 'iphoneos': - return 'ios' - if platform == 'macosx': - if extension == '.framework': - return 'mac_framework' - return 'mac' - raise ValueError('unknown bundle type %s for %s' % (extension, platform)) - - @property - def kind(self): - return self._kind - - @property - def path(self): - return self._path - - @property - def contents_dir(self): - if self._kind == 'mac': - return os.path.join(self.path, 'Contents') - if self._kind == 'mac_framework': - return os.path.join(self.path, 'Versions/A') - return self.path - - @property - def executable_dir(self): - if self._kind == 'mac': - return os.path.join(self.contents_dir, 'MacOS') - return self.contents_dir - - @property - def resources_dir(self): - if self._kind == 'mac' or self._kind == 'mac_framework': - return os.path.join(self.contents_dir, 'Resources') - return self.path - - @property - def info_plist_path(self): - if self._kind == 'mac_framework': - return os.path.join(self.resources_dir, 'Info.plist') - return os.path.join(self.contents_dir, 'Info.plist') - - @property - def signature_dir(self): - return os.path.join(self.contents_dir, '_CodeSignature') - - @property - def identifier(self): - return self._data['CFBundleIdentifier'] - - @property - def binary_name(self): - return self._data['CFBundleExecutable'] - - @property - def binary_path(self): - return os.path.join(self.executable_dir, self.binary_name) - - def Validate(self, expected_mappings): - """Checks that keys in the bundle have the expected value. + """Loads property list file at |plist_path|. Args: - expected_mappings: a dictionary of string to object, each mapping will - be looked up in the bundle data to check it has the same value (missing - values will be ignored) + plist_path: path to the property list file to load. Returns: - A dictionary of the key with a different value between expected_mappings - and the content of the bundle (i.e. errors) so that caller can format the - error message. The dictionary will be empty if there are no errors. + The content of the property list file as a python object. """ - errors = {} - for key, expected_value in expected_mappings.items(): - if key in self._data: - value = self._data[key] - if value != expected_value: - errors[key] = (value, expected_value) - return errors - - -class ProvisioningProfile(object): - """Wraps a mobile provisioning profile file.""" - - def __init__(self, provisioning_profile_path): - """Initializes the ProvisioningProfile with data from profile file.""" - self._path = provisioning_profile_path - self._data = ReadPlistFromString( - subprocess.check_output([ - 'xcrun', 'security', 'cms', '-D', '-u', 'certUsageAnyCA', '-i', - provisioning_profile_path - ])) - - @property - def path(self): - return self._path - - @property - def team_identifier(self): - return self._data.get('TeamIdentifier', [''])[0] - - @property - def name(self): - return self._data.get('Name', '') - - @property - def application_identifier_pattern(self): - return self._data.get('Entitlements', {}).get('application-identifier', '') + if sys.version_info.major == 2: + return plistlib.readPlistFromString( + subprocess.check_output( + ['xcrun', 'plutil', '-convert', 'xml1', '-o', '-', plist_path])) + else: + with open(plist_path, 'rb') as fp: + return plistlib.load(fp) - @property - def application_identifier_prefix(self): - return self._data.get('ApplicationIdentifierPrefix', [''])[0] - @property - def entitlements(self): - return self._data.get('Entitlements', {}) - - @property - def expiration_date(self): - return self._data.get('ExpirationDate', datetime.datetime.now()) +def CreateSymlink(value, location): + """Creates symlink with value at location if the target exists.""" + target = os.path.join(os.path.dirname(location), value) + if os.path.exists(location): + os.unlink(location) + os.symlink(value, location) - def ValidToSignBundle(self, bundle_identifier): - """Checks whether the provisioning profile can sign bundle_identifier. - Args: - bundle_identifier: the identifier of the bundle that needs to be signed. +class Bundle(object): + """Wraps a bundle.""" + + def __init__(self, bundle_path, platform): + """Initializes the Bundle object with data from bundle Info.plist + file.""" + self._path = bundle_path + self._kind = Bundle.Kind(platform, os.path.splitext(bundle_path)[-1]) + self._data = None + + def Load(self): + self._data = LoadPlistFile(self.info_plist_path) + + @staticmethod + def Kind(platform, extension): + if platform == 'appletvsimulator' or platform == 'appletvos': + return 'tvos' + if platform == 'iphonesimulator' or platform == 'iphoneos': + return 'ios' + if platform == 'macosx': + if extension == '.framework': + return 'mac_framework' + return 'mac' + raise ValueError('unknown bundle type %s for %s' % + (extension, platform)) + + @property + def kind(self): + return self._kind + + @property + def path(self): + return self._path + + @property + def contents_dir(self): + if self._kind == 'mac': + return os.path.join(self.path, 'Contents') + if self._kind == 'mac_framework': + return os.path.join(self.path, 'Versions/A') + return self.path + + @property + def executable_dir(self): + if self._kind == 'mac': + return os.path.join(self.contents_dir, 'MacOS') + return self.contents_dir + + @property + def resources_dir(self): + if self._kind == 'mac' or self._kind == 'mac_framework': + return os.path.join(self.contents_dir, 'Resources') + return self.path + + @property + def info_plist_path(self): + if self._kind == 'mac_framework': + return os.path.join(self.resources_dir, 'Info.plist') + return os.path.join(self.contents_dir, 'Info.plist') + + @property + def signature_dir(self): + return os.path.join(self.contents_dir, '_CodeSignature') + + @property + def identifier(self): + return self._data['CFBundleIdentifier'] + + @property + def binary_name(self): + return self._data['CFBundleExecutable'] + + @property + def binary_path(self): + return os.path.join(self.executable_dir, self.binary_name) + + def Validate(self, expected_mappings): + """Checks that keys in the bundle have the expected value. + + Args: + expected_mappings: a dictionary of string to object, each mapping + will be looked up in the bundle data to check it has the same value + (missing values will be ignored) + + Returns: + A dictionary of the key with a different value between + expected_mappings and the content of the bundle (i.e. errors) so + that caller can format the error message. The dictionary will be + empty if there are no errors. + """ + errors = {} + for key, expected_value in expected_mappings.items(): + if key in self._data: + value = self._data[key] + if value != expected_value: + errors[key] = (value, expected_value) + return errors - Returns: - True if the mobile provisioning profile can be used to sign a bundle - with the corresponding bundle_identifier, False otherwise. - """ - return fnmatch.fnmatch( - '%s.%s' % (self.application_identifier_prefix, bundle_identifier), - self.application_identifier_pattern) - def Install(self, installation_path): - """Copies mobile provisioning profile info to |installation_path|.""" - shutil.copy2(self.path, installation_path) +class ProvisioningProfile(object): + """Wraps a mobile provisioning profile file.""" + + def __init__(self, provisioning_profile_path): + """Initializes the ProvisioningProfile with data from profile file.""" + self._path = provisioning_profile_path + self._data = ReadPlistFromString( + subprocess.check_output([ + 'xcrun', 'security', 'cms', '-D', '-u', 'certUsageAnyCA', '-i', + provisioning_profile_path + ])) + + @property + def path(self): + return self._path + + @property + def team_identifier(self): + return self._data.get('TeamIdentifier', [''])[0] + + @property + def name(self): + return self._data.get('Name', '') + + @property + def application_identifier_pattern(self): + return self._data.get('Entitlements', {}).get('application-identifier', + '') + + @property + def application_identifier_prefix(self): + return self._data.get('ApplicationIdentifierPrefix', [''])[0] + + @property + def entitlements(self): + return self._data.get('Entitlements', {}) + + @property + def expiration_date(self): + return self._data.get('ExpirationDate', datetime.datetime.now()) + + def ValidToSignBundle(self, bundle_identifier): + """Checks whether the provisioning profile can sign bundle_identifier. + + Args: + bundle_identifier: the identifier of the bundle that needs to be + signed. + + Returns: + True if the mobile provisioning profile can be used to sign a bundle + with the corresponding bundle_identifier, False otherwise. + """ + return fnmatch.fnmatch( + '%s.%s' % (self.application_identifier_prefix, bundle_identifier), + self.application_identifier_pattern) + + def Install(self, installation_path): + """Copies mobile provisioning profile info to |installation_path|.""" + shutil.copy2(self.path, installation_path) class Entitlements(object): - """Wraps an Entitlement plist file.""" + """Wraps an Entitlement plist file.""" - def __init__(self, entitlements_path): - """Initializes Entitlements object from entitlement file.""" - self._path = entitlements_path - self._data = LoadPlistFile(self._path) + def __init__(self, entitlements_path): + """Initializes Entitlements object from entitlement file.""" + self._path = entitlements_path + self._data = LoadPlistFile(self._path) - @property - def path(self): - return self._path + @property + def path(self): + return self._path - def ExpandVariables(self, substitutions): - self._data = self._ExpandVariables(self._data, substitutions) + def ExpandVariables(self, substitutions): + self._data = self._ExpandVariables(self._data, substitutions) - def _ExpandVariables(self, data, substitutions): - if isinstance(data, basestring_compat): - for key, substitution in substitutions.items(): - data = data.replace('$(%s)' % (key,), substitution) - return data + def _ExpandVariables(self, data, substitutions): + if isinstance(data, basestring_compat): + for key, substitution in substitutions.items(): + data = data.replace('$(%s)' % (key,), substitution) + return data - if isinstance(data, dict): - for key, value in data.items(): - data[key] = self._ExpandVariables(value, substitutions) - return data + if isinstance(data, dict): + for key, value in data.items(): + data[key] = self._ExpandVariables(value, substitutions) + return data - if isinstance(data, list): - for i, value in enumerate(data): - data[i] = self._ExpandVariables(value, substitutions) + if isinstance(data, list): + for i, value in enumerate(data): + data[i] = self._ExpandVariables(value, substitutions) - return data + return data - def LoadDefaults(self, defaults): - for key, value in defaults.items(): - if key not in self._data: - self._data[key] = value + def LoadDefaults(self, defaults): + for key, value in defaults.items(): + if key not in self._data: + self._data[key] = value - def WriteTo(self, target_path): - with open(target_path, 'wb') as fp: - if sys.version_info.major == 2: - plistlib.writePlist(self._data, fp) - else: - plistlib.dump(self._data, fp) + def WriteTo(self, target_path): + with open(target_path, 'wb') as fp: + if sys.version_info.major == 2: + plistlib.writePlist(self._data, fp) + else: + plistlib.dump(self._data, fp) def FindProvisioningProfile(bundle_identifier, required): - """Finds mobile provisioning profile to use to sign bundle. - - Args: - bundle_identifier: the identifier of the bundle to sign. - - Returns: - The ProvisioningProfile object that can be used to sign the Bundle - object or None if no matching provisioning profile was found. - """ - provisioning_profile_paths = glob.glob( - os.path.join(GetProvisioningProfilesDir(), '*.mobileprovision')) - - # Iterate over all installed mobile provisioning profiles and filter those - # that can be used to sign the bundle, ignoring expired ones. - now = datetime.datetime.now() - valid_provisioning_profiles = [] - one_hour = datetime.timedelta(0, 3600) - for provisioning_profile_path in provisioning_profile_paths: - provisioning_profile = ProvisioningProfile(provisioning_profile_path) - if provisioning_profile.expiration_date - now < one_hour: - sys.stderr.write( - 'Warning: ignoring expired provisioning profile: %s.\n' % - provisioning_profile_path) - continue - if provisioning_profile.ValidToSignBundle(bundle_identifier): - valid_provisioning_profiles.append(provisioning_profile) - - if not valid_provisioning_profiles: - if required: - sys.stderr.write( - 'Error: no mobile provisioning profile found for "%s".\n' % - bundle_identifier) - sys.exit(1) - return None - - # Select the most specific mobile provisioning profile, i.e. the one with - # the longest application identifier pattern (prefer the one with the latest - # expiration date as a secondary criteria). - selected_provisioning_profile = max( - valid_provisioning_profiles, - key=lambda p: (len(p.application_identifier_pattern), p.expiration_date)) - - one_week = datetime.timedelta(7) - if selected_provisioning_profile.expiration_date - now < 2 * one_week: - sys.stderr.write( - 'Warning: selected provisioning profile will expire soon: %s' % - selected_provisioning_profile.path) - return selected_provisioning_profile + """Finds mobile provisioning profile to use to sign bundle. + + Args: + bundle_identifier: the identifier of the bundle to sign. + + Returns: + The ProvisioningProfile object that can be used to sign the Bundle + object or None if no matching provisioning profile was found. + """ + provisioning_profile_paths = glob.glob( + os.path.join(GetProvisioningProfilesDir(), '*.mobileprovision')) + + # Iterate over all installed mobile provisioning profiles and filter those + # that can be used to sign the bundle, ignoring expired ones. + now = datetime.datetime.now() + valid_provisioning_profiles = [] + one_hour = datetime.timedelta(0, 3600) + for provisioning_profile_path in provisioning_profile_paths: + provisioning_profile = ProvisioningProfile(provisioning_profile_path) + if provisioning_profile.expiration_date - now < one_hour: + sys.stderr.write( + 'Warning: ignoring expired provisioning profile: %s.\n' % + provisioning_profile_path) + continue + if provisioning_profile.ValidToSignBundle(bundle_identifier): + valid_provisioning_profiles.append(provisioning_profile) + + if not valid_provisioning_profiles: + if required: + sys.stderr.write( + 'Error: no mobile provisioning profile found for "%s".\n' % + bundle_identifier) + sys.exit(1) + return None + + # Select the most specific mobile provisioning profile, i.e. the one with + # the longest application identifier pattern (prefer the one with the latest + # expiration date as a secondary criteria). + selected_provisioning_profile = max( + valid_provisioning_profiles, + key=lambda p: + (len(p.application_identifier_pattern), p.expiration_date)) + + one_week = datetime.timedelta(7) + if selected_provisioning_profile.expiration_date - now < 2 * one_week: + sys.stderr.write( + 'Warning: selected provisioning profile will expire soon: %s' % + selected_provisioning_profile.path) + return selected_provisioning_profile def CodeSignBundle(bundle_path, identity, extra_args): - process = subprocess.Popen( - ['xcrun', 'codesign', '--force', '--sign', identity, '--timestamp=none'] + - list(extra_args) + [bundle_path], - stderr=subprocess.PIPE, - universal_newlines=True) - _, stderr = process.communicate() - if process.returncode: - sys.stderr.write(stderr) - sys.exit(process.returncode) - for line in stderr.splitlines(): - if line.endswith(': replacing existing signature'): - # Ignore warning about replacing existing signature as this should only - # happen when re-signing system frameworks (and then it is expected). - continue - sys.stderr.write(line) - sys.stderr.write('\n') + process = subprocess.Popen([ + 'xcrun', 'codesign', '--force', '--sign', identity, '--timestamp=none' + ] + list(extra_args) + [bundle_path], + stderr=subprocess.PIPE, + universal_newlines=True) + _, stderr = process.communicate() + if process.returncode: + sys.stderr.write(stderr) + sys.exit(process.returncode) + for line in stderr.splitlines(): + if line.endswith(': replacing existing signature'): + # Ignore warning about replacing existing signature as this should only + # happen when re-signing system frameworks (and then it is expected). + continue + sys.stderr.write(line) + sys.stderr.write('\n') def InstallSystemFramework(framework_path, bundle_path, args): - """Install framework from |framework_path| to |bundle| and code-re-sign it.""" - installed_framework_path = os.path.join( - bundle_path, 'Frameworks', os.path.basename(framework_path)) + """Install framework from |framework_path| to |bundle| and code-re-sign + it.""" + installed_framework_path = os.path.join(bundle_path, 'Frameworks', + os.path.basename(framework_path)) - if os.path.isfile(framework_path): - shutil.copy(framework_path, installed_framework_path) - elif os.path.isdir(framework_path): - if os.path.exists(installed_framework_path): - shutil.rmtree(installed_framework_path) - shutil.copytree(framework_path, installed_framework_path) + if os.path.isfile(framework_path): + shutil.copy(framework_path, installed_framework_path) + elif os.path.isdir(framework_path): + if os.path.exists(installed_framework_path): + shutil.rmtree(installed_framework_path) + shutil.copytree(framework_path, installed_framework_path) - CodeSignBundle(installed_framework_path, args.identity, - ['--deep', '--preserve-metadata=identifier,entitlements,flags']) + CodeSignBundle( + installed_framework_path, args.identity, + ['--deep', '--preserve-metadata=identifier,entitlements,flags']) def GenerateEntitlements(path, provisioning_profile, bundle_identifier): - """Generates an entitlements file. - - Args: - path: path to the entitlements template file - provisioning_profile: ProvisioningProfile object to use, may be None - bundle_identifier: identifier of the bundle to sign. - """ - entitlements = Entitlements(path) - if provisioning_profile: - entitlements.LoadDefaults(provisioning_profile.entitlements) - app_identifier_prefix = \ - provisioning_profile.application_identifier_prefix + '.' - else: - app_identifier_prefix = '*.' - entitlements.ExpandVariables({ - 'CFBundleIdentifier': bundle_identifier, - 'AppIdentifierPrefix': app_identifier_prefix, - }) - return entitlements + """Generates an entitlements file. + + Args: + path: path to the entitlements template file + provisioning_profile: ProvisioningProfile object to use, may be None + bundle_identifier: identifier of the bundle to sign. + """ + entitlements = Entitlements(path) + if provisioning_profile: + entitlements.LoadDefaults(provisioning_profile.entitlements) + app_identifier_prefix = \ + provisioning_profile.application_identifier_prefix + '.' + else: + app_identifier_prefix = '*.' + entitlements.ExpandVariables({ + 'CFBundleIdentifier': bundle_identifier, + 'AppIdentifierPrefix': app_identifier_prefix, + }) + return entitlements def GenerateBundleInfoPlist(bundle, plist_compiler, partial_plist): - """Generates the bundle Info.plist for a list of partial .plist files. - - Args: - bundle: a Bundle instance - plist_compiler: string, path to the Info.plist compiler - partial_plist: list of path to partial .plist files to merge - """ - - # Filter empty partial .plist files (this happens if an application - # does not compile any asset catalog, in which case the partial .plist - # file from the asset catalog compilation step is just a stamp file). - filtered_partial_plist = [] - for plist in partial_plist: - plist_size = os.stat(plist).st_size - if plist_size: - filtered_partial_plist.append(plist) - - # Invoke the plist_compiler script. It needs to be a python script. - subprocess.check_call([ - 'python3', - plist_compiler, - 'merge', - '-f', - 'binary1', - '-o', - bundle.info_plist_path, - ] + filtered_partial_plist) + """Generates the bundle Info.plist for a list of partial .plist files. + + Args: + bundle: a Bundle instance + plist_compiler: string, path to the Info.plist compiler + partial_plist: list of path to partial .plist files to merge + """ + + # Filter empty partial .plist files (this happens if an application + # does not compile any asset catalog, in which case the partial .plist + # file from the asset catalog compilation step is just a stamp file). + filtered_partial_plist = [] + for plist in partial_plist: + plist_size = os.stat(plist).st_size + if plist_size: + filtered_partial_plist.append(plist) + + # Invoke the plist_compiler script. It needs to be a python script. + subprocess.check_call([ + 'python3', + plist_compiler, + 'merge', + '-f', + 'binary1', + '-o', + bundle.info_plist_path, + ] + filtered_partial_plist) class Action(object): - """Class implementing one action supported by the script.""" + """Class implementing one action supported by the script.""" - @classmethod - def Register(cls, subparsers): - parser = subparsers.add_parser(cls.name, help=cls.help) - parser.set_defaults(func=cls._Execute) - cls._Register(parser) + @classmethod + def Register(cls, subparsers): + parser = subparsers.add_parser(cls.name, help=cls.help) + parser.set_defaults(func=cls._Execute) + cls._Register(parser) class CodeSignBundleAction(Action): - """Class implementing the code-sign-bundle action.""" - - name = 'code-sign-bundle' - help = 'perform code signature for a bundle' - - @staticmethod - def _Register(parser): - parser.add_argument( - '--entitlements', '-e', dest='entitlements_path', - help='path to the entitlements file to use') - parser.add_argument( - 'path', help='path to the iOS bundle to codesign') - parser.add_argument( - '--identity', '-i', required=True, - help='identity to use to codesign') - parser.add_argument( - '--binary', '-b', required=True, - help='path to the iOS bundle binary') - parser.add_argument( - '--framework', '-F', action='append', default=[], dest='frameworks', - help='install and resign system framework') - parser.add_argument( - '--disable-code-signature', action='store_true', dest='no_signature', - help='disable code signature') - parser.add_argument( - '--disable-embedded-mobileprovision', action='store_false', - default=True, dest='embedded_mobileprovision', - help='disable finding and embedding mobileprovision') - parser.add_argument( - '--platform', '-t', required=True, - help='platform the signed bundle is targeting') - parser.add_argument( - '--partial-info-plist', '-p', action='append', default=[], - help='path to partial Info.plist to merge to create bundle Info.plist') - parser.add_argument( - '--plist-compiler-path', '-P', action='store', - help='path to the plist compiler script (for --partial-info-plist)') - parser.set_defaults(no_signature=False) - - @staticmethod - def _Execute(args): - if not args.identity: - args.identity = '-' - - bundle = Bundle(args.path, args.platform) - - if args.partial_info_plist: - GenerateBundleInfoPlist(bundle, args.plist_compiler_path, - args.partial_info_plist) - - # The bundle Info.plist may have been updated by GenerateBundleInfoPlist() - # above. Load the bundle information from Info.plist after the modification - # have been written to disk. - bundle.Load() - - # According to Apple documentation, the application binary must be the same - # as the bundle name without the .app suffix. See crbug.com/740476 for more - # information on what problem this can cause. - # - # To prevent this class of error, fail with an error if the binary name is - # incorrect in the Info.plist as it is not possible to update the value in - # Info.plist at this point (the file has been copied by a different target - # and ninja would consider the build dirty if it was updated). - # - # Also checks that the name of the bundle is correct too (does not cause the - # build to be considered dirty, but still terminate the script in case of an - # incorrect bundle name). - # - # Apple documentation is available at: - # https://developer.apple.com/library/content/documentation/CoreFoundation/Conceptual/CFBundles/BundleTypes/BundleTypes.html - bundle_name = os.path.splitext(os.path.basename(bundle.path))[0] - errors = bundle.Validate({ - 'CFBundleName': bundle_name, - 'CFBundleExecutable': bundle_name, - }) - if errors: - for key in sorted(errors): - value, expected_value = errors[key] - sys.stderr.write('%s: error: %s value incorrect: %s != %s\n' % ( - bundle.path, key, value, expected_value)) - sys.stderr.flush() - sys.exit(1) - - # Delete existing embedded mobile provisioning. - embedded_provisioning_profile = os.path.join( - bundle.path, 'embedded.mobileprovision') - if os.path.isfile(embedded_provisioning_profile): - os.unlink(embedded_provisioning_profile) - - # Delete existing code signature. - if os.path.exists(bundle.signature_dir): - shutil.rmtree(bundle.signature_dir) - - # Install system frameworks if requested. - for framework_path in args.frameworks: - InstallSystemFramework(framework_path, args.path, args) - - # Copy main binary into bundle. - if not os.path.isdir(bundle.executable_dir): - os.makedirs(bundle.executable_dir) - shutil.copy(args.binary, bundle.binary_path) - - if bundle.kind == 'mac_framework': - # Create Versions/Current -> Versions/A symlink - CreateSymlink('A', os.path.join(bundle.path, 'Versions/Current')) - - # Create $binary_name -> Versions/Current/$binary_name symlink - CreateSymlink(os.path.join('Versions/Current', bundle.binary_name), - os.path.join(bundle.path, bundle.binary_name)) - - # Create optional symlinks. - for name in ('Headers', 'Resources', 'Modules'): - target = os.path.join(bundle.path, 'Versions/A', name) - if os.path.exists(target): - CreateSymlink(os.path.join('Versions/Current', name), - os.path.join(bundle.path, name)) - else: - obsolete_path = os.path.join(bundle.path, name) - if os.path.exists(obsolete_path): - os.unlink(obsolete_path) - - if args.no_signature: - return - - codesign_extra_args = [] - - if args.embedded_mobileprovision: - # Find mobile provisioning profile and embeds it into the bundle (if a - # code signing identify has been provided, fails if no valid mobile - # provisioning is found). - provisioning_profile_required = args.identity != '-' - provisioning_profile = FindProvisioningProfile( - bundle.identifier, provisioning_profile_required) - if provisioning_profile and args.platform != 'iphonesimulator': - provisioning_profile.Install(embedded_provisioning_profile) - - if args.entitlements_path is not None: - temporary_entitlements_file = \ - tempfile.NamedTemporaryFile(suffix='.xcent') - codesign_extra_args.extend( - ['--entitlements', temporary_entitlements_file.name]) - - entitlements = GenerateEntitlements( - args.entitlements_path, provisioning_profile, bundle.identifier) - entitlements.WriteTo(temporary_entitlements_file.name) - - CodeSignBundle(bundle.path, args.identity, codesign_extra_args) + """Class implementing the code-sign-bundle action.""" + + name = 'code-sign-bundle' + help = 'perform code signature for a bundle' + + @staticmethod + def _Register(parser): + parser.add_argument('--entitlements', + '-e', + dest='entitlements_path', + help='path to the entitlements file to use') + parser.add_argument('path', help='path to the iOS bundle to codesign') + parser.add_argument('--identity', + '-i', + required=True, + help='identity to use to codesign') + parser.add_argument('--binary', + '-b', + required=True, + help='path to the iOS bundle binary') + parser.add_argument('--framework', + '-F', + action='append', + default=[], + dest='frameworks', + help='install and resign system framework') + parser.add_argument('--disable-code-signature', + action='store_true', + dest='no_signature', + help='disable code signature') + parser.add_argument( + '--disable-embedded-mobileprovision', + action='store_false', + default=True, + dest='embedded_mobileprovision', + help='disable finding and embedding mobileprovision') + parser.add_argument('--platform', + '-t', + required=True, + help='platform the signed bundle is targeting') + parser.add_argument( + '--partial-info-plist', + '-p', + action='append', + default=[], + help= + 'path to partial Info.plist to merge to create bundle Info.plist') + parser.add_argument( + '--plist-compiler-path', + '-P', + action='store', + help='path to the plist compiler script (for --partial-info-plist)') + parser.set_defaults(no_signature=False) + + @staticmethod + def _Execute(args): + if not args.identity: + args.identity = '-' + + bundle = Bundle(args.path, args.platform) + + if args.partial_info_plist: + GenerateBundleInfoPlist(bundle, args.plist_compiler_path, + args.partial_info_plist) + + # The bundle Info.plist may have been updated by + # GenerateBundleInfoPlist() above. Load the bundle information from + # Info.plist after the modification have been written to disk. + bundle.Load() + + # According to Apple documentation, the application binary must be the + # same as the bundle name without the .app suffix. See crbug.com/740476 + # for more information on what problem this can cause. + # + # To prevent this class of error, fail with an error if the binary name + # is incorrect in the Info.plist as it is not possible to update the + # value in Info.plist at this point (the file has been copied by a + # different target and ninja would consider the build dirty if it was + # updated). + # + # Also checks that the name of the bundle is correct too (does not cause + # the build to be considered dirty, but still terminate the script in + # case of an incorrect bundle name). + # + # Apple documentation is available at: + # https://developer.apple.com/library/content/documentation/CoreFoundation/Conceptual/CFBundles/BundleTypes/BundleTypes.html + bundle_name = os.path.splitext(os.path.basename(bundle.path))[0] + errors = bundle.Validate({ + 'CFBundleName': bundle_name, + 'CFBundleExecutable': bundle_name, + }) + if errors: + for key in sorted(errors): + value, expected_value = errors[key] + sys.stderr.write('%s: error: %s value incorrect: %s != %s\n' % + (bundle.path, key, value, expected_value)) + sys.stderr.flush() + sys.exit(1) + + # Delete existing embedded mobile provisioning. + embedded_provisioning_profile = os.path.join( + bundle.path, 'embedded.mobileprovision') + if os.path.isfile(embedded_provisioning_profile): + os.unlink(embedded_provisioning_profile) + + # Delete existing code signature. + if os.path.exists(bundle.signature_dir): + shutil.rmtree(bundle.signature_dir) + + # Install system frameworks if requested. + for framework_path in args.frameworks: + InstallSystemFramework(framework_path, args.path, args) + + # Copy main binary into bundle. + if not os.path.isdir(bundle.executable_dir): + os.makedirs(bundle.executable_dir) + shutil.copy(args.binary, bundle.binary_path) + + if bundle.kind == 'mac_framework': + # Create Versions/Current -> Versions/A symlink + CreateSymlink('A', os.path.join(bundle.path, 'Versions/Current')) + + # Create $binary_name -> Versions/Current/$binary_name symlink + CreateSymlink(os.path.join('Versions/Current', bundle.binary_name), + os.path.join(bundle.path, bundle.binary_name)) + + # Create optional symlinks. + for name in ('Headers', 'Resources', 'Modules'): + target = os.path.join(bundle.path, 'Versions/A', name) + if os.path.exists(target): + CreateSymlink(os.path.join('Versions/Current', name), + os.path.join(bundle.path, name)) + else: + obsolete_path = os.path.join(bundle.path, name) + if os.path.exists(obsolete_path): + os.unlink(obsolete_path) + + if args.no_signature: + return + + codesign_extra_args = [] + + if args.embedded_mobileprovision: + # Find mobile provisioning profile and embeds it into the bundle (if + # a code signing identify has been provided, fails if no valid + # mobile provisioning is found). + provisioning_profile_required = args.identity != '-' + provisioning_profile = FindProvisioningProfile( + bundle.identifier, provisioning_profile_required) + if provisioning_profile and args.platform != 'iphonesimulator': + provisioning_profile.Install(embedded_provisioning_profile) + + if args.entitlements_path is not None: + temporary_entitlements_file = \ + tempfile.NamedTemporaryFile(suffix='.xcent') + codesign_extra_args.extend( + ['--entitlements', temporary_entitlements_file.name]) + + entitlements = GenerateEntitlements(args.entitlements_path, + provisioning_profile, + bundle.identifier) + entitlements.WriteTo(temporary_entitlements_file.name) + + CodeSignBundle(bundle.path, args.identity, codesign_extra_args) class CodeSignFileAction(Action): - """Class implementing code signature for a single file.""" - - name = 'code-sign-file' - help = 'code-sign a single file' - - @staticmethod - def _Register(parser): - parser.add_argument( - 'path', help='path to the file to codesign') - parser.add_argument( - '--identity', '-i', required=True, - help='identity to use to codesign') - parser.add_argument( - '--output', '-o', - help='if specified copy the file to that location before signing it') - parser.set_defaults(sign=True) - - @staticmethod - def _Execute(args): - if not args.identity: - args.identity = '-' - - install_path = args.path - if args.output: - - if os.path.isfile(args.output): - os.unlink(args.output) - elif os.path.isdir(args.output): - shutil.rmtree(args.output) - - if os.path.isfile(args.path): - shutil.copy(args.path, args.output) - elif os.path.isdir(args.path): - shutil.copytree(args.path, args.output) - - install_path = args.output - - CodeSignBundle(install_path, args.identity, - ['--deep', '--preserve-metadata=identifier,entitlements']) + """Class implementing code signature for a single file.""" + + name = 'code-sign-file' + help = 'code-sign a single file' + + @staticmethod + def _Register(parser): + parser.add_argument('path', help='path to the file to codesign') + parser.add_argument('--identity', + '-i', + required=True, + help='identity to use to codesign') + parser.add_argument( + '--output', + '-o', + help='if specified copy the file to that location before signing it' + ) + parser.set_defaults(sign=True) + + @staticmethod + def _Execute(args): + if not args.identity: + args.identity = '-' + + install_path = args.path + if args.output: + + if os.path.isfile(args.output): + os.unlink(args.output) + elif os.path.isdir(args.output): + shutil.rmtree(args.output) + + if os.path.isfile(args.path): + shutil.copy(args.path, args.output) + elif os.path.isdir(args.path): + shutil.copytree(args.path, args.output) + + install_path = args.output + + CodeSignBundle( + install_path, args.identity, + ['--deep', '--preserve-metadata=identifier,entitlements']) class GenerateEntitlementsAction(Action): - """Class implementing the generate-entitlements action.""" - - name = 'generate-entitlements' - help = 'generate entitlements file' - - @staticmethod - def _Register(parser): - parser.add_argument( - '--entitlements', '-e', dest='entitlements_path', - help='path to the entitlements file to use') - parser.add_argument( - 'path', help='path to the entitlements file to generate') - parser.add_argument( - '--info-plist', '-p', required=True, - help='path to the bundle Info.plist') - - @staticmethod - def _Execute(args): - info_plist = LoadPlistFile(args.info_plist) - bundle_identifier = info_plist['CFBundleIdentifier'] - provisioning_profile = FindProvisioningProfile(bundle_identifier, False) - entitlements = GenerateEntitlements( - args.entitlements_path, provisioning_profile, bundle_identifier) - entitlements.WriteTo(args.path) + """Class implementing the generate-entitlements action.""" + + name = 'generate-entitlements' + help = 'generate entitlements file' + + @staticmethod + def _Register(parser): + parser.add_argument('--entitlements', + '-e', + dest='entitlements_path', + help='path to the entitlements file to use') + parser.add_argument('path', + help='path to the entitlements file to generate') + parser.add_argument('--info-plist', + '-p', + required=True, + help='path to the bundle Info.plist') + + @staticmethod + def _Execute(args): + info_plist = LoadPlistFile(args.info_plist) + bundle_identifier = info_plist['CFBundleIdentifier'] + provisioning_profile = FindProvisioningProfile(bundle_identifier, False) + entitlements = GenerateEntitlements(args.entitlements_path, + provisioning_profile, + bundle_identifier) + entitlements.WriteTo(args.path) class FindProvisioningProfileAction(Action): - """Class implementing the find-codesign-identity action.""" - - name = 'find-provisioning-profile' - help = 'find provisioning profile for use by Xcode project generator' - - @staticmethod - def _Register(parser): - parser.add_argument('--bundle-id', - '-b', - required=True, - help='bundle identifier') - - @staticmethod - def _Execute(args): - provisioning_profile_info = {} - provisioning_profile = FindProvisioningProfile(args.bundle_id, False) - for key in ('team_identifier', 'name'): - if provisioning_profile: - provisioning_profile_info[key] = getattr(provisioning_profile, key) - else: - provisioning_profile_info[key] = '' - print(json.dumps(provisioning_profile_info)) + """Class implementing the find-codesign-identity action.""" + + name = 'find-provisioning-profile' + help = 'find provisioning profile for use by Xcode project generator' + + @staticmethod + def _Register(parser): + parser.add_argument('--bundle-id', + '-b', + required=True, + help='bundle identifier') + + @staticmethod + def _Execute(args): + provisioning_profile_info = {} + provisioning_profile = FindProvisioningProfile(args.bundle_id, False) + for key in ('team_identifier', 'name'): + if provisioning_profile: + provisioning_profile_info[key] = getattr( + provisioning_profile, key) + else: + provisioning_profile_info[key] = '' + print(json.dumps(provisioning_profile_info)) def Main(): - # Cache this codec so that plistlib can find it. See - # https://crbug.com/999461#c12 for more details. - codecs.lookup('utf-8') + # Cache this codec so that plistlib can find it. See + # https://crbug.com/999461#c12 for more details. + codecs.lookup('utf-8') - parser = argparse.ArgumentParser('codesign iOS bundles') - subparsers = parser.add_subparsers() + parser = argparse.ArgumentParser('codesign iOS bundles') + subparsers = parser.add_subparsers() - actions = [ - CodeSignBundleAction, - CodeSignFileAction, - GenerateEntitlementsAction, - FindProvisioningProfileAction, - ] + actions = [ + CodeSignBundleAction, + CodeSignFileAction, + GenerateEntitlementsAction, + FindProvisioningProfileAction, + ] - for action in actions: - action.Register(subparsers) + for action in actions: + action.Register(subparsers) - args = parser.parse_args() - args.func(args) + args = parser.parse_args() + args.func(args) if __name__ == '__main__': - sys.exit(Main()) + sys.exit(Main()) diff --git a/build/ios/find_signing_identity.py b/build/ios/find_signing_identity.py index d6ef368b..d4e734a9 100644 --- a/build/ios/find_signing_identity.py +++ b/build/ios/find_signing_identity.py @@ -12,78 +12,81 @@ def Redact(value, from_nth_char=5): - """Redact value past the N-th character.""" - return value[:from_nth_char] + '*' * (len(value) - from_nth_char) + """Redact value past the N-th character.""" + return value[:from_nth_char] + '*' * (len(value) - from_nth_char) class Identity(object): - """Represents a valid identity.""" + """Represents a valid identity.""" - def __init__(self, identifier, name, team): - self.identifier = identifier - self.name = name - self.team = team + def __init__(self, identifier, name, team): + self.identifier = identifier + self.name = name + self.team = team - def redacted(self): - return Identity(Redact(self.identifier), self.name, Redact(self.team)) + def redacted(self): + return Identity(Redact(self.identifier), self.name, Redact(self.team)) - def format(self): - return '%s: "%s (%s)"' % (self.identifier, self.name, self.team) + def format(self): + return '%s: "%s (%s)"' % (self.identifier, self.name, self.team) def ListIdentities(): - return subprocess.check_output([ - 'xcrun', - 'security', - 'find-identity', - '-v', - '-p', - 'codesigning', - ]).decode('utf8') + return subprocess.check_output([ + 'xcrun', + 'security', + 'find-identity', + '-v', + '-p', + 'codesigning', + ]).decode('utf8') def FindValidIdentity(pattern): - """Find all identities matching the pattern.""" - lines = list(l.strip() for l in ListIdentities().splitlines()) - # Look for something like "2) XYZ "iPhone Developer: Name (ABC)"" - regex = re.compile('[0-9]+\) ([A-F0-9]+) "([^"(]*) \(([^)"]*)\)"') + """Find all identities matching the pattern.""" + lines = list(l.strip() for l in ListIdentities().splitlines()) + # Look for something like "2) XYZ "iPhone Developer: Name (ABC)"" + regex = re.compile('[0-9]+\) ([A-F0-9]+) "([^"(]*) \(([^)"]*)\)"') - result = [] - for line in lines: - res = regex.match(line) - if res is None: - continue - if pattern is None or pattern in res.group(2): - result.append(Identity(*res.groups())) - return result + result = [] + for line in lines: + res = regex.match(line) + if res is None: + continue + if pattern is None or pattern in res.group(2): + result.append(Identity(*res.groups())) + return result def Main(args): - parser = argparse.ArgumentParser('codesign iOS bundles') - parser.add_argument( - '--identity-description', required=True, dest='pattern', - help='Text description used to select the code signing identity.') - parsed = parser.parse_args(args) - - identities = FindValidIdentity(parsed.pattern) - if len(identities) == 1: - print(identities[0].identifier, end='') - return 0 - - all_identities = FindValidIdentity(None) - - print('Automatic code signing identity selection was enabled but could not') - print('find exactly one codesigning identity matching "%s".' % parsed.pattern) - print('') - print('Check that the keychain is accessible and that there is exactly one') - print('valid codesigning identity matching the pattern. Here is the parsed') - print('output of `xcrun security find-identity -v -p codesigning`:') - print() - for i, identity in enumerate(all_identities): - print(' %d) %s' % (i + 1, identity.redacted().format())) - print(' %d valid identities found' % (len(all_identities))) - return 1 + parser = argparse.ArgumentParser('codesign iOS bundles') + parser.add_argument( + '--identity-description', + required=True, + dest='pattern', + help='Text description used to select the code signing identity.') + parsed = parser.parse_args(args) + + identities = FindValidIdentity(parsed.pattern) + if len(identities) == 1: + print(identities[0].identifier, end='') + return 0 + + all_identities = FindValidIdentity(None) + + print('Automatic code signing identity selection was enabled but could not') + print('find exactly one codesigning identity matching "%s".' % + parsed.pattern) + print('') + print('Check that the keychain is accessible and that there is exactly one') + print('valid codesigning identity matching the pattern. Here is the parsed') + print('output of `xcrun security find-identity -v -p codesigning`:') + print() + for i, identity in enumerate(all_identities): + print(' %d) %s' % (i + 1, identity.redacted().format())) + print(' %d valid identities found' % (len(all_identities))) + return 1 if __name__ == '__main__': - sys.exit(Main(sys.argv[1:])) + sys.exit(Main(sys.argv[1:])) diff --git a/build/ios/ios_sdk.gni b/build/ios/ios_sdk.gni index 535f678b..dff747f1 100644 --- a/build/ios/ios_sdk.gni +++ b/build/ios/ios_sdk.gni @@ -28,6 +28,10 @@ declare_args() { # default is only there for compatibility reasons and will be removed (see # crbug.com/1138425 for more details). target_environment = "" + + # The iOS-based platform being targeted. Possible values: "iphoneos", "tvos". + # The default is "iphoneos". + target_platform = "iphoneos" } if (target_environment == "") { @@ -42,12 +46,22 @@ use_ios_simulator = target_environment == "simulator" if (ios_sdk_path == "") { # Compute default target. - if (use_ios_simulator) { - ios_sdk_name = "iphonesimulator" - ios_sdk_platform = "iPhoneSimulator" - } else { - ios_sdk_name = "iphoneos" - ios_sdk_platform = "iPhoneOS" + if (target_platform == "iphoneos") { + if (use_ios_simulator) { + ios_sdk_name = "iphonesimulator" + ios_sdk_platform = "iPhoneSimulator" + } else { + ios_sdk_name = "iphoneos" + ios_sdk_platform = "iPhoneOS" + } + } else if (target_platform == "tvos") { + if (use_ios_simulator) { + ios_sdk_name = "appletvsimulator" + ios_sdk_platform = "AppleTVSimulator" + } else { + ios_sdk_name = "appletvos" + ios_sdk_platform = "AppleTVOS" + } } ios_sdk_info_args = [ "--get_sdk_info" ] diff --git a/build/ios/plist_util.py b/build/ios/plist_util.py index fb480a28..7873a788 100644 --- a/build/ios/plist_util.py +++ b/build/ios/plist_util.py @@ -10,19 +10,18 @@ import subprocess import sys import tempfile -import shlex if sys.version_info.major < 3: - basestring_compat = basestring + basestring_compat = basestring else: - basestring_compat = str + basestring_compat = str # Xcode substitutes variables like ${PRODUCT_NAME} or $(PRODUCT_NAME) when -# compiling Info.plist. It also supports supports modifiers like :identifier -# or :rfc1034identifier. SUBSTITUTION_REGEXP_LIST is a list of regular -# expressions matching a variable substitution pattern with an optional -# modifier, while INVALID_CHARACTER_REGEXP matches all characters that are -# not valid in an "identifier" value (used when applying the modifier). +# compiling Info.plist. It also supports supports modifiers like :identifier or +# :rfc1034identifier. SUBSTITUTION_REGEXP_LIST is a list of regular expressions +# matching a variable substitution pattern with an optional modifier, while +# INVALID_CHARACTER_REGEXP matches all characters that are not valid in an +# "identifier" value (used when applying the modifier). INVALID_CHARACTER_REGEXP = re.compile(r'[_/\s]') SUBSTITUTION_REGEXP_LIST = ( re.compile(r'\$\{(?P[^}]*?)(?P:[^}]*)?\}'), @@ -31,235 +30,244 @@ class SubstitutionError(Exception): - def __init__(self, key): - super(SubstitutionError, self).__init__() - self.key = key - def __str__(self): - return "SubstitutionError: {}".format(self.key) + def __init__(self, key): + super(SubstitutionError, self).__init__() + self.key = key + def __str__(self): + return "SubstitutionError: {}".format(self.key) -def InterpolateString(value, substitutions): - """Interpolates variable references into |value| using |substitutions|. - - Inputs: - value: a string - substitutions: a mapping of variable names to values - - Returns: - A new string with all variables references ${VARIABLES} replaced by their - value in |substitutions|. Raises SubstitutionError if a variable has no - substitution. - """ - - def repl(match): - variable = match.group('id') - if variable not in substitutions: - raise SubstitutionError(variable) - # Some values need to be identifier and thus the variables references may - # contains :modifier attributes to indicate how they should be converted - # to identifiers ("identifier" replaces all invalid characters by '_' and - # "rfc1034identifier" replaces them by "-" to make valid URI too). - modifier = match.group('modifier') - if modifier == ':identifier': - return INVALID_CHARACTER_REGEXP.sub('_', substitutions[variable]) - elif modifier == ':rfc1034identifier': - return INVALID_CHARACTER_REGEXP.sub('-', substitutions[variable]) - else: - return substitutions[variable] - for substitution_regexp in SUBSTITUTION_REGEXP_LIST: - value = substitution_regexp.sub(repl, value) - return value +def InterpolateString(value, substitutions): + """Interpolates variable references into |value| using |substitutions|. + + Args: + value: a string + substitutions: a mapping of variable names to values + + Returns: + A new string with all variables references ${VARIABLES} replaced by + their value in |substitutions|. Raises SubstitutionError if a variable + has no substitution. + """ + + def repl(match): + variable = match.group('id') + if variable not in substitutions: + raise SubstitutionError(variable) + # Some values need to be identifier and thus the variables references + # may contains :modifier attributes to indicate how they should be + # converted to identifiers ("identifier" replaces all invalid characters + # by '_' and "rfc1034identifier" replaces them by "-" to make valid URI + # too). + modifier = match.group('modifier') + if modifier == ':identifier': + return INVALID_CHARACTER_REGEXP.sub('_', substitutions[variable]) + elif modifier == ':rfc1034identifier': + return INVALID_CHARACTER_REGEXP.sub('-', substitutions[variable]) + else: + return substitutions[variable] + + for substitution_regexp in SUBSTITUTION_REGEXP_LIST: + value = substitution_regexp.sub(repl, value) + return value def Interpolate(value, substitutions): - """Interpolates variable references into |value| using |substitutions|. - - Inputs: - value: a value, can be a dictionary, list, string or other - substitutions: a mapping of variable names to values - - Returns: - A new value with all variables references ${VARIABLES} replaced by their - value in |substitutions|. Raises SubstitutionError if a variable has no - substitution. - """ - if isinstance(value, dict): - return {k: Interpolate(v, substitutions) for k, v in value.items()} - if isinstance(value, list): - return [Interpolate(v, substitutions) for v in value] - if isinstance(value, basestring_compat): - return InterpolateString(value, substitutions) - return value + """Interpolates variable references into |value| using |substitutions|. + + Args: + value: a value, can be a dictionary, list, string or other + substitutions: a mapping of variable names to values + + Returns: + A new value with all variables references ${VARIABLES} replaced by their + value in |substitutions|. Raises SubstitutionError if a variable has no + substitution. + """ + if isinstance(value, dict): + return {k: Interpolate(v, substitutions) for k, v in value.items()} + if isinstance(value, list): + return [Interpolate(v, substitutions) for v in value] + if isinstance(value, basestring_compat): + return InterpolateString(value, substitutions) + return value def LoadPList(path): - """Loads Plist at |path| and returns it as a dictionary.""" - if sys.version_info.major == 2: - fd, name = tempfile.mkstemp() - try: - subprocess.check_call(['plutil', '-convert', 'xml1', '-o', name, path]) - with os.fdopen(fd, 'rb') as f: - return plistlib.readPlist(f) - finally: - os.unlink(name) - else: - with open(path, 'rb') as f: - return plistlib.load(f) + """Loads Plist at |path| and returns it as a dictionary.""" + if sys.version_info.major == 2: + fd, name = tempfile.mkstemp() + try: + subprocess.check_call( + ['plutil', '-convert', 'xml1', '-o', name, path]) + with os.fdopen(fd, 'rb') as f: + return plistlib.readPlist(f) + finally: + os.unlink(name) + else: + with open(path, 'rb') as f: + return plistlib.load(f) def SavePList(path, format, data): - """Saves |data| as a Plist to |path| in the specified |format|.""" - # The below does not replace the destination file but update it in place, - # so if more than one hardlink points to destination all of them will be - # modified. This is not what is expected, so delete destination file if - # it does exist. - if os.path.exists(path): - os.unlink(path) - if sys.version_info.major == 2: - fd, name = tempfile.mkstemp() - try: - with os.fdopen(fd, 'wb') as f: - plistlib.writePlist(data, f) - subprocess.check_call(['plutil', '-convert', format, '-o', path, name]) - finally: - os.unlink(name) - else: - with open(path, 'wb') as f: - plist_format = {'binary1': plistlib.FMT_BINARY, 'xml1': plistlib.FMT_XML} - plistlib.dump(data, f, fmt=plist_format[format]) + """Saves |data| as a Plist to |path| in the specified |format|.""" + # The below does not replace the destination file but update it in place, + # so if more than one hardlink points to destination all of them will be + # modified. This is not what is expected, so delete destination file if + # it does exist. + if os.path.exists(path): + os.unlink(path) + if sys.version_info.major == 2: + fd, name = tempfile.mkstemp() + try: + with os.fdopen(fd, 'wb') as f: + plistlib.writePlist(data, f) + subprocess.check_call( + ['plutil', '-convert', format, '-o', path, name]) + finally: + os.unlink(name) + else: + with open(path, 'wb') as f: + plist_format = { + 'binary1': plistlib.FMT_BINARY, + 'xml1': plistlib.FMT_XML + } + plistlib.dump(data, f, fmt=plist_format[format]) def MergePList(plist1, plist2): - """Merges |plist1| with |plist2| recursively. - - Creates a new dictionary representing a Property List (.plist) files by - merging the two dictionary |plist1| and |plist2| recursively (only for - dictionary values). List value will be concatenated. - - Args: - plist1: a dictionary representing a Property List (.plist) file - plist2: a dictionary representing a Property List (.plist) file - - Returns: - A new dictionary representing a Property List (.plist) file by merging - |plist1| with |plist2|. If any value is a dictionary, they are merged - recursively, otherwise |plist2| value is used. If values are list, they - are concatenated. - """ - result = plist1.copy() - for key, value in plist2.items(): - if isinstance(value, dict): - old_value = result.get(key) - if isinstance(old_value, dict): - value = MergePList(old_value, value) - if isinstance(value, list): - value = plist1.get(key, []) + plist2.get(key, []) - result[key] = value - return result + """Merges |plist1| with |plist2| recursively. + + Creates a new dictionary representing a Property List (.plist) files by + merging the two dictionary |plist1| and |plist2| recursively (only for + dictionary values). List value will be concatenated. + + Args: + plist1: a dictionary representing a Property List (.plist) file plist2: + a dictionary representing a Property List (.plist) file + + Returns: + A new dictionary representing a Property List (.plist) file by merging + |plist1| with |plist2|. If any value is a dictionary, they are merged + recursively, otherwise |plist2| value is used. If values are list, they + are concatenated. + """ + result = plist1.copy() + for key, value in plist2.items(): + if isinstance(value, dict): + old_value = result.get(key) + if isinstance(old_value, dict): + value = MergePList(old_value, value) + if isinstance(value, list): + value = plist1.get(key, []) + plist2.get(key, []) + result[key] = value + return result class Action(object): - """Class implementing one action supported by the script.""" + """Class implementing one action supported by the script.""" - @classmethod - def Register(cls, subparsers): - parser = subparsers.add_parser(cls.name, help=cls.help) - parser.set_defaults(func=cls._Execute) - cls._Register(parser) + @classmethod + def Register(cls, subparsers): + parser = subparsers.add_parser(cls.name, help=cls.help) + parser.set_defaults(func=cls._Execute) + cls._Register(parser) class MergeAction(Action): - """Class to merge multiple plist files.""" - - name = 'merge' - help = 'merge multiple plist files' - - @staticmethod - def _Register(parser): - parser.add_argument('-o', - '--output', - required=True, - help='path to the output plist file') - parser.add_argument('-f', - '--format', - required=True, - choices=('xml1', 'binary1'), - help='format of the plist file to generate') - parser.add_argument( - '-x', - '--xcode-version', - help='version of Xcode, ignored (can be used to force rebuild)') - parser.add_argument('path', nargs="+", help='path to plist files to merge') - - @staticmethod - def _Execute(args): - data = {} - for filename in args.path: - data = MergePList(data, LoadPList(filename)) - SavePList(args.output, args.format, data) + """Class to merge multiple plist files.""" + + name = 'merge' + help = 'merge multiple plist files' + + @staticmethod + def _Register(parser): + parser.add_argument('-o', + '--output', + required=True, + help='path to the output plist file') + parser.add_argument('-f', + '--format', + required=True, + choices=('xml1', 'binary1'), + help='format of the plist file to generate') + parser.add_argument( + '-x', + '--xcode-version', + help='version of Xcode, ignored (can be used to force rebuild)') + parser.add_argument('path', + nargs="+", + help='path to plist files to merge') + + @staticmethod + def _Execute(args): + data = {} + for filename in args.path: + data = MergePList(data, LoadPList(filename)) + SavePList(args.output, args.format, data) class SubstituteAction(Action): - """Class implementing the variable substitution in a plist file.""" - - name = 'substitute' - help = 'perform pattern substitution in a plist file' - - @staticmethod - def _Register(parser): - parser.add_argument('-o', - '--output', - required=True, - help='path to the output plist file') - parser.add_argument('-t', - '--template', - required=True, - help='path to the template file') - parser.add_argument('-s', - '--substitution', - action='append', - default=[], - help='substitution rule in the format key=value') - parser.add_argument('-f', - '--format', - required=True, - choices=('xml1', 'binary1'), - help='format of the plist file to generate') - parser.add_argument( - '-x', - '--xcode-version', - help='version of Xcode, ignored (can be used to force rebuild)') - - @staticmethod - def _Execute(args): - substitutions = {} - for substitution in args.substitution: - key, value = substitution.split('=', 1) - substitutions[key] = value - data = Interpolate(LoadPList(args.template), substitutions) - SavePList(args.output, args.format, data) + """Class implementing the variable substitution in a plist file.""" + + name = 'substitute' + help = 'perform pattern substitution in a plist file' + + @staticmethod + def _Register(parser): + parser.add_argument('-o', + '--output', + required=True, + help='path to the output plist file') + parser.add_argument('-t', + '--template', + required=True, + help='path to the template file') + parser.add_argument('-s', + '--substitution', + action='append', + default=[], + help='substitution rule in the format key=value') + parser.add_argument('-f', + '--format', + required=True, + choices=('xml1', 'binary1'), + help='format of the plist file to generate') + parser.add_argument( + '-x', + '--xcode-version', + help='version of Xcode, ignored (can be used to force rebuild)') + + @staticmethod + def _Execute(args): + substitutions = {} + for substitution in args.substitution: + key, value = substitution.split('=', 1) + substitutions[key] = value + data = Interpolate(LoadPList(args.template), substitutions) + SavePList(args.output, args.format, data) def Main(): - # Cache this codec so that plistlib can find it. See - # https://crbug.com/1005190#c2 for more details. - codecs.lookup('utf-8') + # Cache this codec so that plistlib can find it. See + # https://crbug.com/1005190#c2 for more details. + codecs.lookup('utf-8') - parser = argparse.ArgumentParser(description='manipulate plist files') - subparsers = parser.add_subparsers() + parser = argparse.ArgumentParser(description='manipulate plist files') + subparsers = parser.add_subparsers() - for action in [MergeAction, SubstituteAction]: - action.Register(subparsers) + for action in [MergeAction, SubstituteAction]: + action.Register(subparsers) - args = parser.parse_args() - args.func(args) + args = parser.parse_args() + args.func(args) if __name__ == '__main__': - # TODO(https://crbug.com/941669): Temporary workaround until all scripts use - # python3 by default. - if sys.version_info[0] < 3: - os.execvp('python3', ['python3'] + sys.argv) - sys.exit(Main()) + # TODO(https://crbug.com/941669): Temporary workaround until all scripts use + # python3 by default. + if sys.version_info[0] < 3: + os.execvp('python3', ['python3'] + sys.argv) + sys.exit(Main()) diff --git a/build/ios/rules.gni b/build/ios/rules.gni index 42801e42..1f3019c2 100644 --- a/build/ios/rules.gni +++ b/build/ios/rules.gni @@ -105,9 +105,7 @@ template("create_signed_bundle") { # Bundle ID should respect rfc1034 and replace _ with -. _xcode_product_bundle_id = - string_replace("$ios_app_bundle_id_prefix.$_output_name", - "_", - "-") + string_replace("$ios_app_bundle_id_prefix.$_output_name", "_", "-") xcode_extra_attributes = { IPHONEOS_DEPLOYMENT_TARGET = ios_deployment_target @@ -627,10 +625,16 @@ template("ios_xcuitest_test_runner_bundle") { if (xcode_version_int >= 1300) { extra_system_frameworks += [ "$ios_sdk_platform_path/Developer/Library/PrivateFrameworks/XCTestCore.framework", - "$ios_sdk_platform_path/Developer/Library/PrivateFrameworks/XCUIAutomation.framework", "$ios_sdk_platform_path/Developer/Library/PrivateFrameworks/XCUnit.framework", "$ios_sdk_platform_path/Developer/usr/lib/libXCTestSwiftSupport.dylib", ] + + # Xcode 16.3 moved XCUIAutomation.framework + if (xcode_version_int < 1630) { + extra_system_frameworks += [ "$ios_sdk_platform_path/Developer/Library/PrivateFrameworks/XCUIAutomation.framework" ] + } else { + extra_system_frameworks += [ "$ios_sdk_platform_path/Developer/Library/Frameworks/XCUIAutomation.framework" ] + } } # XCTestSupport framework is required as of Xcode 14.3 or later. @@ -690,6 +694,6 @@ template("ios_xcuitest_test") { template("ios_test_runner_xcuitest") { ios_xcuitest_test(target_name) { - forward_variables_from(invoker, "*", ["data_deps"]) + forward_variables_from(invoker, "*", [ "data_deps" ]) } } diff --git a/build/ios/sdk_info.py b/build/ios/sdk_info.py index 3a1e8f58..1ba48912 100755 --- a/build/ios/sdk_info.py +++ b/build/ios/sdk_info.py @@ -1,3 +1,5 @@ +#!/usr/bin/env python3 + # Copyright 2014 The Chromium Authors # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. @@ -9,168 +11,169 @@ import itertools import os import plistlib -import re import subprocess import sys if sys.version_info.major < 3: - basestring_compat = basestring + basestring_compat = basestring else: - basestring_compat = str + basestring_compat = str # src directory ROOT_SRC_DIR = os.path.dirname( os.path.dirname(os.path.dirname(os.path.dirname( os.path.realpath(__file__))))) -# This script prints information about the build system, the operating -# system and the iOS or Mac SDK (depending on the platform "iphonesimulator", +# This script prints information about the build system, the operating system +# and the iOS or Mac SDK (depending on the platform "iphonesimulator", # "iphoneos" or "macosx" generally). def LoadPList(path): - """Loads Plist at |path| and returns it as a dictionary.""" - # Cloned from //build/apple/plist_util.py. - if sys.version_info.major == 2: - return plistlib.readPlist(path) - with open(path, 'rb') as f: - return plistlib.load(f) + """Loads Plist at |path| and returns it as a dictionary.""" + # Cloned from //build/apple/plist_util.py. + if sys.version_info.major == 2: + return plistlib.readPlist(path) + with open(path, 'rb') as f: + return plistlib.load(f) def SplitVersion(version): - """Splits the Xcode version to 3 values. + """Splits the Xcode version to 3 values. - >>> list(SplitVersion('8.2.1.1')) - ['8', '2', '1'] - >>> list(SplitVersion('9.3')) - ['9', '3', '0'] - >>> list(SplitVersion('10.0')) - ['10', '0', '0'] - """ - version = version.split('.') - return itertools.islice(itertools.chain(version, itertools.repeat('0')), 0, 3) + >>> list(SplitVersion('8.2.1.1')) + ['8', '2', '1'] + >>> list(SplitVersion('9.3')) + ['9', '3', '0'] + >>> list(SplitVersion('10.0')) + ['10', '0', '0'] + """ + version = version.split('.') + return itertools.islice(itertools.chain(version, itertools.repeat('0')), 0, + 3) def FormatVersion(version): - """Converts Xcode version to a format required for DTXcode in Info.plist + """Converts Xcode version to a format required for DTXcode in Info.plist - >>> FormatVersion('8.2.1') - '0821' - >>> FormatVersion('9.3') - '0930' - >>> FormatVersion('10.0') - '1000' - """ - major, minor, patch = SplitVersion(version) - return ('%2s%s%s' % (major, minor, patch)).replace(' ', '0') + >>> FormatVersion('8.2.1') + '0821' + >>> FormatVersion('9.3') + '0930' + >>> FormatVersion('10.0') + '1000' + """ + major, minor, patch = SplitVersion(version) + return ('%2s%s%s' % (major, minor, patch)).replace(' ', '0') def FillXcodeVersion(settings, developer_dir): - """Fills the Xcode version and build number into |settings|.""" - if developer_dir: - xcode_version_plist_path = os.path.join(developer_dir, - 'Contents/version.plist') - version_plist = LoadPList(xcode_version_plist_path) - settings['xcode_version'] = FormatVersion( - version_plist['CFBundleShortVersionString']) + """Fills the Xcode version and build number into |settings|.""" + if developer_dir: + xcode_version_plist_path = os.path.join(developer_dir, + 'Contents/version.plist') + version_plist = LoadPList(xcode_version_plist_path) + settings['xcode_version'] = FormatVersion( + version_plist['CFBundleShortVersionString']) + settings['xcode_version_int'] = int(settings['xcode_version'], 10) + settings['xcode_build'] = version_plist['ProductBuildVersion'] + return + + lines = subprocess.check_output(['xcodebuild', + '-version']).decode('UTF-8').splitlines() + settings['xcode_version'] = FormatVersion(lines[0].split()[-1]) settings['xcode_version_int'] = int(settings['xcode_version'], 10) - settings['xcode_build'] = version_plist['ProductBuildVersion'] - return - - lines = subprocess.check_output(['xcodebuild', - '-version']).decode('UTF-8').splitlines() - settings['xcode_version'] = FormatVersion(lines[0].split()[-1]) - settings['xcode_version_int'] = int(settings['xcode_version'], 10) - settings['xcode_build'] = lines[-1].split()[-1] + settings['xcode_build'] = lines[-1].split()[-1] def FillMachineOSBuild(settings): - """Fills OS build number into |settings|.""" - machine_os_build = subprocess.check_output(['sw_vers', '-buildVersion' - ]).decode('UTF-8').strip() - settings['machine_os_build'] = machine_os_build + """Fills OS build number into |settings|.""" + machine_os_build = subprocess.check_output(['sw_vers', '-buildVersion' + ]).decode('UTF-8').strip() + settings['machine_os_build'] = machine_os_build def FillSDKPathAndVersion(settings, platform, xcode_version): - """Fills the SDK path and version for |platform| into |settings|.""" - settings['sdk_path'] = subprocess.check_output( - ['xcrun', '-sdk', platform, '--show-sdk-path']).decode('UTF-8').strip() - settings['sdk_version'] = subprocess.check_output( - ['xcrun', '-sdk', platform, - '--show-sdk-version']).decode('UTF-8').strip() - settings['sdk_platform_path'] = subprocess.check_output( - ['xcrun', '-sdk', platform, - '--show-sdk-platform-path']).decode('UTF-8').strip() - settings['sdk_build'] = subprocess.check_output( - ['xcrun', '-sdk', platform, - '--show-sdk-build-version']).decode('UTF-8').strip() - settings['toolchains_path'] = os.path.join( - subprocess.check_output(['xcode-select', - '-print-path']).decode('UTF-8').strip(), - 'Toolchains/XcodeDefault.xctoolchain') + """Fills the SDK path and version for |platform| into |settings|.""" + settings['sdk_path'] = subprocess.check_output( + ['xcrun', '-sdk', platform, '--show-sdk-path']).decode('UTF-8').strip() + settings['sdk_version'] = subprocess.check_output( + ['xcrun', '-sdk', platform, + '--show-sdk-version']).decode('UTF-8').strip() + settings['sdk_platform_path'] = subprocess.check_output( + ['xcrun', '-sdk', platform, + '--show-sdk-platform-path']).decode('UTF-8').strip() + settings['sdk_build'] = subprocess.check_output( + ['xcrun', '-sdk', platform, + '--show-sdk-build-version']).decode('UTF-8').strip() + settings['toolchains_path'] = os.path.join( + subprocess.check_output(['xcode-select', + '-print-path']).decode('UTF-8').strip(), + 'Toolchains/XcodeDefault.xctoolchain') def CreateXcodeSymlinkAt(src, dst): - """Create symlink to Xcode directory at target location.""" + """Create symlink to Xcode directory at target location.""" - if not os.path.isdir(dst): - os.makedirs(dst) + if not os.path.isdir(dst): + os.makedirs(dst) - dst = os.path.join(dst, os.path.basename(src)) - updated_value = '//' + os.path.relpath(dst, ROOT_SRC_DIR) + dst = os.path.join(dst, os.path.basename(src)) + updated_value = '//' + os.path.relpath(dst, ROOT_SRC_DIR) - # Update the symlink only if it is different from the current destination. - if os.path.islink(dst): - current_src = os.readlink(dst) - if current_src == src: - return updated_value - os.unlink(dst) - sys.stderr.write('existing symlink %s points %s; want %s. Removed.' % - (dst, current_src, src)) - os.symlink(src, dst) - return updated_value + # Update the symlink only if it is different from the current destination. + if os.path.islink(dst): + current_src = os.readlink(dst) + if current_src == src: + return updated_value + os.unlink(dst) + sys.stderr.write('existing symlink %s points %s; want %s. Removed.' % + (dst, current_src, src)) + os.symlink(src, dst) + return updated_value if __name__ == '__main__': - doctest.testmod() - - parser = argparse.ArgumentParser() - parser.add_argument("--developer_dir", dest="developer_dir", required=False) - parser.add_argument("--get_sdk_info", - action="store_true", - dest="get_sdk_info", - default=False, - help="Returns SDK info in addition to xcode info.") - parser.add_argument("--get_machine_info", - action="store_true", - dest="get_machine_info", - default=False, - help="Returns machine info in addition to xcode info.") - parser.add_argument("--create_symlink_at", - action="store", - dest="create_symlink_at", - help="Create symlink of SDK at given location and " - "returns the symlinked paths as SDK info instead " - "of the original location.") - args, unknownargs = parser.parse_known_args() - if args.developer_dir: - os.environ['DEVELOPER_DIR'] = args.developer_dir - - if len(unknownargs) != 1: - sys.stderr.write('usage: %s [iphoneos|iphonesimulator|macosx]\n' % - os.path.basename(sys.argv[0])) - sys.exit(1) - - settings = {} - FillMachineOSBuild(settings) - FillXcodeVersion(settings, args.developer_dir) - if args.get_sdk_info: - FillSDKPathAndVersion(settings, unknownargs[0], settings['xcode_version']) - - for key in sorted(settings): - value = settings[key] - if args.create_symlink_at and '_path' in key: - value = CreateXcodeSymlinkAt(value, args.create_symlink_at) - if isinstance(value, basestring_compat): - value = '"%s"' % value - print('%s=%s' % (key, value)) + doctest.testmod() + + parser = argparse.ArgumentParser() + parser.add_argument("--developer_dir", dest="developer_dir", required=False) + parser.add_argument("--get_sdk_info", + action="store_true", + dest="get_sdk_info", + default=False, + help="Returns SDK info in addition to xcode info.") + parser.add_argument("--get_machine_info", + action="store_true", + dest="get_machine_info", + default=False, + help="Returns machine info in addition to xcode info.") + parser.add_argument("--create_symlink_at", + action="store", + dest="create_symlink_at", + help="Create symlink of SDK at given location and " + "returns the symlinked paths as SDK info instead " + "of the original location.") + args, unknownargs = parser.parse_known_args() + if args.developer_dir: + os.environ['DEVELOPER_DIR'] = args.developer_dir + + if len(unknownargs) != 1: + sys.stderr.write('usage: %s [iphoneos|iphonesimulator|macosx]\n' % + os.path.basename(sys.argv[0])) + sys.exit(1) + + settings = {} + FillMachineOSBuild(settings) + FillXcodeVersion(settings, args.developer_dir) + if args.get_sdk_info: + FillSDKPathAndVersion(settings, unknownargs[0], + settings['xcode_version']) + + for key in sorted(settings): + value = settings[key] + if args.create_symlink_at and '_path' in key: + value = CreateXcodeSymlinkAt(value, args.create_symlink_at) + if isinstance(value, basestring_compat): + value = '"%s"' % value + print('%s=%s' % (key, value)) diff --git a/build/ios/strip_arm64e.py b/build/ios/strip_arm64e.py index aefc35ab..f7fe9faf 100644 --- a/build/ios/strip_arm64e.py +++ b/build/ios/strip_arm64e.py @@ -1,6 +1,7 @@ # Copyright 2020 The Chromium Authors # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. + """Strip arm64e architecture from a binary if present.""" import argparse @@ -11,59 +12,61 @@ def check_output(command): - """Returns the output from |command| or propagates error, quitting script.""" - process = subprocess.Popen( - command, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - outs, errs = process.communicate() - if process.returncode: - sys.stderr.write('error: command failed with retcode %d: %s\n\n' % - (process.returncode, ' '.join(map(repr, command)))) - sys.stderr.write(errs.decode('UTF-8', errors='ignore')) - sys.exit(process.returncode) - return outs.decode('UTF-8') + """Returns the output from |command| or propagates error, quitting + script.""" + process = subprocess.Popen(command, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + outs, errs = process.communicate() + if process.returncode: + sys.stderr.write('error: command failed with retcode %d: %s\n\n' % + (process.returncode, ' '.join(map(repr, command)))) + sys.stderr.write(errs.decode('UTF-8', errors='ignore')) + sys.exit(process.returncode) + return outs.decode('UTF-8') def check_call(command): - """Invokes |command| or propagates error.""" - check_output(command) + """Invokes |command| or propagates error.""" + check_output(command) def parse_args(args): - """Parses the command-line.""" - parser = argparse.ArgumentParser() - parser.add_argument('--input', required=True, help='Path to input binary') - parser.add_argument('--output', required=True, help='Path to output binary') - return parser.parse_args(args) + """Parses the command-line.""" + parser = argparse.ArgumentParser() + parser.add_argument('--input', required=True, help='Path to input binary') + parser.add_argument('--output', required=True, help='Path to output binary') + return parser.parse_args(args) def get_archs(path): - """Extracts the architectures present in binary at |path|.""" - outputs = check_output(["xcrun", "lipo", "-info", os.path.abspath(path)]) - return outputs.split(': ')[-1].split() + """Extracts the architectures present in binary at |path|.""" + outputs = check_output(["xcrun", "lipo", "-info", os.path.abspath(path)]) + return outputs.split(': ')[-1].split() def main(args): - parsed = parse_args(args) + parsed = parse_args(args) - outdir = os.path.dirname(parsed.output) - if not os.path.isdir(outdir): - os.makedirs(outdir) + outdir = os.path.dirname(parsed.output) + if not os.path.isdir(outdir): + os.makedirs(outdir) - if os.path.exists(parsed.output): - os.unlink(parsed.output) + if os.path.exists(parsed.output): + os.unlink(parsed.output) - # As "lipo" fails with an error if asked to remove an architecture that is - # not included, only use it if "arm64e" is present in the binary. Otherwise - # simply copy the file. - if 'arm64e' in get_archs(parsed.input): - check_output([ - "xcrun", "lipo", "-remove", "arm64e", "-output", - os.path.abspath(parsed.output), - os.path.abspath(parsed.input) - ]) - else: - shutil.copy(parsed.input, parsed.output) + # As "lipo" fails with an error if asked to remove an architecture that is + # not included, only use it if "arm64e" is present in the binary. Otherwise + # simply copy the file. + if 'arm64e' in get_archs(parsed.input): + check_output([ + "xcrun", "lipo", "-remove", "arm64e", "-output", + os.path.abspath(parsed.output), + os.path.abspath(parsed.input) + ]) + else: + shutil.copy(parsed.input, parsed.output) if __name__ == '__main__': - main(sys.argv[1:]) + main(sys.argv[1:]) diff --git a/build/win_helper.py b/build/win_helper.py index be977c81..8f7b8913 100644 --- a/build/win_helper.py +++ b/build/win_helper.py @@ -10,221 +10,247 @@ def _RegistryGetValue(key, value): - """Use the winreg module to obtain the value of a registry key. - - Args: - key: The registry key. - value: The particular registry value to read. - Return: - contents of the registry key's value, or None on failure. - """ - try: - root, subkey = key.split('\\', 1) - assert root == 'HKLM' # Only need HKLM for now. - with winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, subkey) as hkey: - return winreg.QueryValueEx(hkey, value)[0] - except WindowsError: - return None + """Use the winreg module to obtain the value of a registry key. + + Args: + key: The registry key. + value: The particular registry value to read. + Return: + contents of the registry key's value, or None on failure. + """ + try: + root, subkey = key.split('\\', 1) + assert root == 'HKLM' # Only need HKLM for now. + with winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, subkey) as hkey: + return winreg.QueryValueEx(hkey, value)[0] + except WindowsError: + return None def _ExtractImportantEnvironment(output_of_set): - """Extracts environment variables required for the toolchain to run from - a textual dump output by the cmd.exe 'set' command.""" - envvars_to_save = ( - 'include', - 'lib', - 'libpath', - 'path', - 'pathext', - 'systemroot', - 'temp', - 'tmp', - ) - env = {} - for line in output_of_set.splitlines(): - line = line.decode("utf-8") - for envvar in envvars_to_save: - if re.match(envvar + '=', line.lower()): - var, setting = line.split('=', 1) - env[var.upper()] = setting - break - for required in ('SYSTEMROOT', 'TEMP', 'TMP'): - if required not in env: - raise Exception('Environment variable "%s" ' - 'required to be set to valid path' % required) - return env + """Extracts environment variables required for the toolchain to run from + a textual dump output by the cmd.exe 'set' command.""" + envvars_to_save = ( + 'include', + 'lib', + 'libpath', + 'path', + 'pathext', + 'systemroot', + 'temp', + 'tmp', + ) + env = {} + for line in output_of_set.splitlines(): + line = line.decode("utf-8") + for envvar in envvars_to_save: + if re.match(envvar + '=', line.lower()): + var, setting = line.split('=', 1) + env[var.upper()] = setting + break + for required in ('SYSTEMROOT', 'TEMP', 'TMP'): + if required not in env: + raise Exception('Environment variable "%s" ' + 'required to be set to valid path' % required) + return env def _FormatAsEnvironmentBlock(envvar_dict): - """Format as an 'environment block' directly suitable for CreateProcess. - Briefly this is a list of key=value\0, terminated by an additional \0. See - CreateProcess() documentation for more details.""" - block = '' - nul = '\0' - for key, value in envvar_dict.items(): - block += key + '=' + value + nul - block += nul - return block + """Format as an 'environment block' directly suitable for CreateProcess. + Briefly this is a list of key=value\0, terminated by an additional \0. See + CreateProcess() documentation for more details.""" + block = '' + nul = '\0' + for key, value in envvar_dict.items(): + block += key + '=' + value + nul + block += nul + return block def _GenerateEnvironmentFiles(install_dir, out_dir, script_path): - """It's not sufficient to have the absolute path to the compiler, linker, etc. - on Windows, as those tools rely on .dlls being in the PATH. We also need to - support both x86 and x64 compilers. Different architectures require a - different compiler binary, and different supporting environment variables - (INCLUDE, LIB, LIBPATH). So, we extract the environment here, wrap all - invocations of compiler tools (cl, link, lib, rc, midl, etc.) to set up the - environment, and then do not prefix the compiler with an absolute path, - instead preferring something like "cl.exe" in the rule which will then run - whichever the environment setup has put in the path.""" - archs = ('x86', 'amd64', 'arm64') - result = [] - for arch in archs: - # Extract environment variables for subprocesses. - args = [os.path.join(install_dir, script_path)] - script_arch_name = arch - if script_path.endswith('SetEnv.cmd') and arch == 'amd64': - script_arch_name = '/x64' - if arch == 'arm64': - script_arch_name = 'x86_arm64' - args.extend((script_arch_name, '&&', 'set')) - popen = subprocess.Popen( - args, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) - variables, _ = popen.communicate() - if popen.returncode != 0: - raise Exception('"%s" failed with error %d' % (args, popen.returncode)) - env = _ExtractImportantEnvironment(variables) - - env_block = _FormatAsEnvironmentBlock(env) - basename = 'environment.' + arch - with open(os.path.join(out_dir, basename), 'wb') as f: - f.write(env_block.encode()) - result.append(basename) - return result + """It's not sufficient to have the absolute path to the compiler, linker, + etc. on Windows, as those tools rely on .dlls being in the PATH. We also + need to support both x86 and x64 compilers. Different architectures require + a different compiler binary, and different supporting environment variables + (INCLUDE, LIB, LIBPATH). So, we extract the environment here, wrap all + invocations of compiler tools (cl, link, lib, rc, midl, etc.) to set up the + environment, and then do not prefix the compiler with an absolute path, + instead preferring something like "cl.exe" in the rule which will then run + whichever the environment setup has put in the path.""" + archs = ('x86', 'amd64', 'arm64') + result = [] + for arch in archs: + # Extract environment variables for subprocesses. + args = [os.path.join(install_dir, script_path)] + script_arch_name = arch + if script_path.endswith('SetEnv.cmd') and arch == 'amd64': + script_arch_name = '/x64' + if arch == 'arm64': + script_arch_name = 'x86_arm64' + args.extend((script_arch_name, '&&', 'set')) + popen = subprocess.Popen(args, + shell=True, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT) + variables, _ = popen.communicate() + if popen.returncode != 0: + raise Exception('"%s" failed with error %d' % + (args, popen.returncode)) + env = _ExtractImportantEnvironment(variables) + + env_block = _FormatAsEnvironmentBlock(env) + basename = 'environment.' + arch + with open(os.path.join(out_dir, basename), 'wb') as f: + f.write(env_block.encode()) + result.append(basename) + return result def _GetEnvAsDict(arch): - """Gets the saved environment from a file for a given architecture.""" - # The environment is saved as an "environment block" (see CreateProcess() - # for details, which is the format required for ninja). We convert to a dict - # here. Drop last 2 NULs, one for list terminator, one for trailing vs. - # separator. - pairs = open(arch).read()[:-2].split('\0') - kvs = [item.split('=', 1) for item in pairs] - return dict(kvs) + """Gets the saved environment from a file for a given architecture.""" + # The environment is saved as an "environment block" (see CreateProcess() + # for details, which is the format required for ninja). We convert to a dict + # here. Drop last 2 NULs, one for list terminator, one for trailing vs. + # separator. + pairs = open(arch).read()[:-2].split('\0') + kvs = [item.split('=', 1) for item in pairs] + return dict(kvs) -class WinTool(object): - def Dispatch(self, args): - """Dispatches a string command to a method.""" - if len(args) < 1: - raise Exception("Not enough arguments") - - method = "Exec%s" % self._CommandifyName(args[0]) - return getattr(self, method)(*args[1:]) - - def _CommandifyName(self, name_string): - """Transforms a tool name like recursive-mirror to RecursiveMirror.""" - return name_string.title().replace('-', '') - - def ExecLinkWrapper(self, arch, *args): - """Filter diagnostic output from link that looks like: - ' Creating library ui.dll.lib and object ui.dll.exp' - This happens when there are exports from the dll or exe. - """ - env = _GetEnvAsDict(arch) +def _SlashSlashes(args): + """Returns args as list, with backslashes instead of slashes in args[0].""" args = list(args) # *args is a tuple by default, which is read-only. args[0] = args[0].replace('/', '\\') - link = subprocess.Popen(args, env=env, shell=True, stdout=subprocess.PIPE) - out, _ = link.communicate() - for line in out.splitlines(): - line = line.decode("utf-8") - if (not line.startswith(' Creating library ') and - not line.startswith('Generating code') and - not line.startswith('Finished generating code')): - print(line) - return link.returncode - - def ExecAsmWrapper(self, arch, *args): - """Filter logo banner from invocations of asm.exe.""" - env = _GetEnvAsDict(arch) - popen = subprocess.Popen(args, env=env, shell=True, - stdout=subprocess.PIPE, stderr=subprocess.STDOUT) - out, _ = popen.communicate() - for line in out.splitlines(): - line = line.decode("utf-8") - if (not line.startswith('Copyright (C) Microsoft Corporation') and - not line.startswith('Microsoft (R) Macro Assembler') and - not line.startswith(' Assembling: ') and - line): - print(line) - return popen.returncode - - def ExecGetVisualStudioData(self, outdir, toolchain_path): - setenv_paths = [ - # cipd packaged SDKs from 10.0.19041.0 onwards. - os.path.join('Windows Kits', '10', 'bin', 'SetEnv.cmd'), - # cipd packaged SDKs prior to 10.0.19041.0. - os.path.join('win_sdk', 'bin', 'SetEnv.cmd'), - ] - - def explicit(): - for setenv_path in setenv_paths: - if os.path.exists(os.path.join(toolchain_path, setenv_path)): - return toolchain_path, setenv_path - - def env(): - from_env = os.environ.get('VSINSTALLDIR') - for setenv_path in setenv_paths: - if from_env and os.path.exists(os.path.join(from_env, setenv_path)): - return from_env, setenv_path - - def autodetect(): - # Try vswhere, which will find VS2017.2+. Note that earlier VS2017s will - # not be found. - vswhere_path = os.path.join(os.environ.get('ProgramFiles(x86)'), - 'Microsoft Visual Studio', 'Installer', 'vswhere.exe') - if os.path.exists(vswhere_path): - installation_path = subprocess.check_output( - [vswhere_path, '-latest', '-property', 'installationPath']).strip() - if installation_path: - return (installation_path.decode("utf-8"), - os.path.join('VC', 'Auxiliary', 'Build', 'vcvarsall.bat')) - - # Otherwise, try VS2015. - version = '14.0' - keys = [r'HKLM\Software\Microsoft\VisualStudio\%s' % version, - r'HKLM\Software\Wow6432Node\Microsoft\VisualStudio\%s' % version] - for key in keys: - path = _RegistryGetValue(key, 'InstallDir') - if not path: - continue - return (os.path.normpath(os.path.join(path, os.pardir, os.pardir)), - os.path.join('VC', 'vcvarsall.bat')) - - def fail(): raise Exception('Visual Studio installation dir not found') - - # Use an explicitly specified toolchain path, if provided and found. - # Otherwise, try using a standard environment variable. Finally, try - # autodetecting using vswhere. - install_dir, script_path = (explicit() or env() or autodetect() or fail()) - - x86_file, x64_file, arm64_file = _GenerateEnvironmentFiles( - install_dir, outdir, script_path) - # gn is unhappy with trailing backslashes. - install_dir = install_dir.rstrip('\\') - result = '''install_dir = "%s" + return args + + +class WinTool(object): + + def Dispatch(self, args): + """Dispatches a string command to a method.""" + if len(args) < 1: + raise Exception("Not enough arguments") + + method = "Exec%s" % self._CommandifyName(args[0]) + return getattr(self, method)(*args[1:]) + + def _CommandifyName(self, name_string): + """Transforms a tool name like recursive-mirror to RecursiveMirror.""" + return name_string.title().replace('-', '') + + def ExecLinkWrapper(self, arch, *args): + """Filter diagnostic output from link that looks like: + ' Creating library ui.dll.lib and object ui.dll.exp' + This happens when there are exports from the dll or exe. + """ + env = _GetEnvAsDict(arch) + args = _SlashSlashes(args) + link = subprocess.Popen(args, + env=env, + shell=True, + stdout=subprocess.PIPE) + out, _ = link.communicate() + for line in out.splitlines(): + line = line.decode("utf-8") + if (not line.startswith(' Creating library ') and + not line.startswith('Generating code') and + not line.startswith('Finished generating code')): + print(line) + return link.returncode + + def ExecAsmWrapper(self, arch, *args): + """Filter logo banner from invocations of asm.exe.""" + env = _GetEnvAsDict(arch) + args = _SlashSlashes(args) + popen = subprocess.Popen(args, + env=env, + shell=True, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT) + out, _ = popen.communicate() + for line in out.splitlines(): + line = line.decode("utf-8") + if (not line.startswith('Copyright (C) Microsoft Corporation') and + not line.startswith('Microsoft (R) Macro Assembler') and + not line.startswith(' Assembling: ') and line): + print(line) + return popen.returncode + + def ExecGetVisualStudioData(self, outdir, toolchain_path): + setenv_paths = [ + # cipd packaged SDKs from 10.0.19041.0 onwards. + os.path.join('Windows Kits', '10', 'bin', 'SetEnv.cmd'), + # cipd packaged SDKs prior to 10.0.19041.0. + os.path.join('win_sdk', 'bin', 'SetEnv.cmd'), + ] + + def explicit(): + for setenv_path in setenv_paths: + if os.path.exists(os.path.join(toolchain_path, setenv_path)): + return toolchain_path, setenv_path + + def env(): + from_env = os.environ.get('VSINSTALLDIR') + for setenv_path in setenv_paths: + if from_env and os.path.exists( + os.path.join(from_env, setenv_path)): + return from_env, setenv_path + + def autodetect(): + # Try vswhere, which will find VS2017.2+. Note that earlier VS2017s + # will not be found. + vswhere_path = os.path.join(os.environ.get('ProgramFiles(x86)'), + 'Microsoft Visual Studio', 'Installer', + 'vswhere.exe') + if os.path.exists(vswhere_path): + installation_path = subprocess.check_output([ + vswhere_path, '-products', '*', '-latest', '-property', + 'installationPath' + ]).strip() + if installation_path: + return (installation_path.decode("utf-8"), + os.path.join('VC', 'Auxiliary', 'Build', + 'vcvarsall.bat')) + + # Otherwise, try VS2015. + version = '14.0' + keys = [ + r'HKLM\Software\Microsoft\VisualStudio\%s' % version, + r'HKLM\Software\Wow6432Node\Microsoft\VisualStudio\%s' % version + ] + for key in keys: + path = _RegistryGetValue(key, 'InstallDir') + if not path: + continue + return (os.path.normpath( + os.path.join(path, os.pardir, os.pardir)), + os.path.join('VC', 'vcvarsall.bat')) + + def fail(): + raise Exception('Visual Studio installation dir not found') + + # Use an explicitly specified toolchain path, if provided and found. + # Otherwise, try using a standard environment variable. Finally, try + # autodetecting using vswhere. + install_dir, script_path = (explicit() or env() or autodetect() or + fail()) + + x86_file, x64_file, arm64_file = _GenerateEnvironmentFiles( + install_dir, outdir, script_path) + # gn is unhappy with trailing backslashes. + install_dir = install_dir.rstrip('\\') + result = '''install_dir = "%s" x86_environment_file = "%s" x64_environment_file = "%s" arm64_environment_file = "%s"''' % (install_dir, x86_file, x64_file, arm64_file) - print(result) - return 0 + print(result) + return 0 - def ExecStamp(self, path): - """Simple stamp command.""" - open(path, 'w').close() - return 0 + def ExecStamp(self, path): + """Simple stamp command.""" + open(path, 'w').close() + return 0 if __name__ == '__main__': - sys.exit(WinTool().Dispatch(sys.argv[1:])) + sys.exit(WinTool().Dispatch(sys.argv[1:])) diff --git a/build/write_buildflag_header.py b/build/write_buildflag_header.py index 99ecb93f..5e5792d2 100755 --- a/build/write_buildflag_header.py +++ b/build/write_buildflag_header.py @@ -7,9 +7,9 @@ # This writes headers for build flags. See buildflag_header.gni for usage of # this system as a whole. # -# The parameters are passed in a response file so we don't have to worry -# about command line lengths. The name of the response file is passed on the -# command line. +# The parameters are passed in a response file so we don't have to worry about +# command line lengths. The name of the response file is passed on the command +# line. # # The format of the response file is: # [--flags ] @@ -50,8 +50,8 @@ def GetOptions(): # The actual output file is inside the gen dir. output = os.path.join(cmdline_options.gen_dir, cmdline_options.output) - # Definition file in GYP is newline separated, in GN they are shell formatted. - # shlex can parse both of these. + # Definition file in GYP is newline separated, in GN they are shell + # formatted. shlex can parse both of these. with open(cmdline_options.definitions, 'r') as def_file: defs = shlex.split(def_file.read()) flags_index = defs.index('--flags')