// Copyright (c) 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include #include "gn/source_file.h" #include "base/logging.h" #include "gn/filesystem_utils.h" #include "gn/source_dir.h" #include "util/build_config.h" namespace { void AssertValueSourceFileString(const std::string& s) { #if defined(OS_WIN) DCHECK(s[0] == '/' || (s.size() > 2 && s[0] != '/' && s[1] == ':' && IsSlash(s[2]))); #else DCHECK(s[0] == '/'); #endif DCHECK(!EndsWithSlash(s)) << s; } bool EndsWithExtension(std::string_view str, std::string_view ext) { return str.size() > ext.size() && str[str.size() - ext.size() - 1] == '.' && !::memcmp(str.data() + str.size() - ext.size(), ext.data(), ext.size()); } SourceFile::Type GetSourceFileType(const std::string& file) { size_t size = file.size(); const char* str = file.data(); // First, single-char extensions. if (size > 2 && str[size - 2] == '.') { switch (str[size - 1]) { case 'c': return SourceFile::SOURCE_C; // .c case 'h': return SourceFile::SOURCE_H; // .h case 'm': return SourceFile::SOURCE_M; // .m case 'o': return SourceFile::SOURCE_O; // .o case 'S': case 's': return SourceFile::SOURCE_S; // .S and .s default: return SourceFile::SOURCE_UNKNOWN; } } // Second, two-char extensions if (size > 3 && str[size - 3] == '.') { #define TAG2(c1, c2) ((unsigned)(c1) | ((unsigned)(c2) << 8)) switch (TAG2(str[size - 2], str[size - 1])) { case TAG2('c', 'c'): return SourceFile::SOURCE_CPP; // .cc case TAG2('g', 'o'): return SourceFile::SOURCE_GO; // .go case TAG2('h', 'h'): return SourceFile::SOURCE_H; // .hh case TAG2('m', 'm'): return SourceFile::SOURCE_MM; // .mm case TAG2('r', 'c'): return SourceFile::SOURCE_RC; // .rc case TAG2('r', 's'): return SourceFile::SOURCE_RS; // .rs default: return SourceFile::SOURCE_UNKNOWN; } #undef TAG2 } if (size > 4 && str[size - 4] == '.') { #define TAG3(c1, c2, c3) \ ((unsigned)(c1) | ((unsigned)(c2) << 8) | ((unsigned)(c3) << 16)) switch (TAG3(str[size - 3], str[size - 2], str[size - 1])) { case TAG3('c', 'p', 'p'): case TAG3('c', 'x', 'x'): case TAG3('c', '+', '+'): return SourceFile::SOURCE_CPP; case TAG3('h', 'p', 'p'): case TAG3('h', 'x', 'x'): case TAG3('i', 'n', 'c'): case TAG3('i', 'p', 'p'): case TAG3('i', 'n', 'l'): return SourceFile::SOURCE_H; case TAG3('a', 's', 'm'): return SourceFile::SOURCE_S; case TAG3('d', 'e', 'f'): return SourceFile::SOURCE_DEF; case TAG3('o', 'b', 'j'): return SourceFile::SOURCE_O; default: return SourceFile::SOURCE_UNKNOWN; } #undef TAG3 } // Other cases if (EndsWithExtension(file, "swift")) return SourceFile::SOURCE_SWIFT; if (EndsWithExtension(file, "swiftmodule")) return SourceFile::SOURCE_SWIFTMODULE; if (EndsWithExtension(file, "modulemap")) return SourceFile::SOURCE_MODULEMAP; return SourceFile::SOURCE_UNKNOWN; } std::string Normalized(std::string value) { DCHECK(!value.empty()); AssertValueSourceFileString(value); NormalizePath(&value); return value; } } // namespace SourceFile::SourceFile(const std::string& value) : SourceFile(StringAtom(Normalized(value))) {} SourceFile::SourceFile(std::string&& value) : SourceFile(StringAtom(Normalized(std::move(value)))) {} SourceFile::SourceFile(StringAtom value) : value_(value) {} SourceFile::Type SourceFile::GetType() const { return GetSourceFileType(value_.str()); } bool SourceFile::IsDefType() const { std::string_view v = value_.str(); return EndsWithExtension(v, "def"); } bool SourceFile::IsObjectType() const { std::string_view v = value_.str(); return EndsWithExtension(v, "o") || EndsWithExtension(v, "obj"); } bool SourceFile::IsModuleMapType() const { std::string_view v = value_.str(); return EndsWithExtension(v, "modulemap"); } bool SourceFile::IsSwiftType() const { std::string_view v = value_.str(); return EndsWithExtension(v, "swift"); } bool SourceFile::IsSwiftModuleType() const { std::string_view v = value_.str(); return EndsWithExtension(v, "swiftmodule"); } std::string SourceFile::GetName() const { if (is_null()) return std::string(); const std::string& value = value_.str(); DCHECK(value.find('/') != std::string::npos); size_t last_slash = value.rfind('/'); return std::string(&value[last_slash + 1], value.size() - last_slash - 1); } SourceDir SourceFile::GetDir() const { if (is_null()) return SourceDir(); const std::string& value = value_.str(); DCHECK(value.find('/') != std::string::npos); size_t last_slash = value.rfind('/'); return SourceDir(value.substr(0, last_slash + 1)); } base::FilePath SourceFile::Resolve(const base::FilePath& source_root) const { return ResolvePath(value_.str(), true, source_root); } void SourceFile::SetValue(const std::string& value) { value_ = StringAtom(value); } SourceFileTypeSet::SourceFileTypeSet() : empty_(true) { memset(flags_, 0, sizeof(bool) * static_cast(SourceFile::SOURCE_NUMTYPES)); } bool SourceFileTypeSet::CSourceUsed() const { return empty_ || Get(SourceFile::SOURCE_CPP) || Get(SourceFile::SOURCE_MODULEMAP) || Get(SourceFile::SOURCE_H) || Get(SourceFile::SOURCE_C) || Get(SourceFile::SOURCE_M) || Get(SourceFile::SOURCE_MM) || Get(SourceFile::SOURCE_RC) || Get(SourceFile::SOURCE_S) || Get(SourceFile::SOURCE_O) || Get(SourceFile::SOURCE_DEF); } bool SourceFileTypeSet::RustSourceUsed() const { return Get(SourceFile::SOURCE_RS); } bool SourceFileTypeSet::GoSourceUsed() const { return Get(SourceFile::SOURCE_GO); } bool SourceFileTypeSet::SwiftSourceUsed() const { return Get(SourceFile::SOURCE_SWIFT); } bool SourceFileTypeSet::MixedSourceUsed() const { return (1 << static_cast(CSourceUsed()) << static_cast(RustSourceUsed()) << static_cast(GoSourceUsed()) << static_cast(SwiftSourceUsed())) > 2; }