blob: f9d816a47ae6a830a6469e2e68a69e8010d9d7a3 [file] [log] [blame]
// 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 <string.h>
#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<int>(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<int>(CSourceUsed())
<< static_cast<int>(RustSourceUsed())
<< static_cast<int>(GoSourceUsed())
<< static_cast<int>(SwiftSourceUsed())) > 2;
}