import '../artifacts.dart';
import '../base/file_system.dart';
import '../build_info.dart';
import '../globals.dart';
import 'build_system.dart';
import 'exceptions.dart';
typedef InputFunction = List<File> Function(Environment environment);
class SourceVisitor {
SourceVisitor(this.environment, [this.inputs = true]);
final Environment environment;
final bool inputs;
final List<File> sources = <File>[];
void visitFunction(InputFunction function) {
sources.addAll(function(environment));
}
void visitPattern(String pattern, bool optional) {
final List<String> segments = <String>[];
final List<String> rawParts = pattern.split('/');
final bool hasWildcard = rawParts.last.contains('*');
String wildcardFile;
if (hasWildcard) {
wildcardFile = rawParts.removeLast();
}
switch (rawParts.first) {
case Environment.kProjectDirectory:
segments.addAll(
fs.path.split(environment.projectDir.resolveSymbolicLinksSync()));
break;
case Environment.kBuildDirectory:
segments.addAll(fs.path.split(
environment.buildDir.resolveSymbolicLinksSync()));
break;
case Environment.kCacheDirectory:
segments.addAll(
fs.path.split(environment.cacheDir.resolveSymbolicLinksSync()));
break;
case Environment.kFlutterRootDirectory:
segments.addAll(
fs.path.split(environment.flutterRootDir.absolute.path));
break;
default:
throw InvalidPatternException(pattern);
}
rawParts.skip(1).forEach(segments.add);
final String filePath = fs.path.joinAll(segments);
if (!hasWildcard) {
if (optional && !fs.isFileSync(filePath)) {
return;
}
sources.add(fs.file(fs.path.normalize(filePath)));
return;
}
final List<String> wildcardSegments = wildcardFile.split('*');
if (wildcardSegments.length > 2) {
throw InvalidPatternException(pattern);
}
if (!fs.directory(filePath).existsSync()) {
throw Exception('$filePath does not exist!');
}
for (FileSystemEntity entity in fs.directory(filePath).listSync()) {
final String filename = fs.path.basename(entity.path);
if (wildcardSegments.isEmpty) {
sources.add(fs.file(entity.absolute));
} else if (wildcardSegments.length == 1) {
if (filename.startsWith(wildcardSegments[0]) ||
filename.endsWith(wildcardSegments[0])) {
sources.add(entity.absolute);
}
} else if (filename.startsWith(wildcardSegments[0])) {
if (filename.substring(wildcardSegments[0].length).endsWith(wildcardSegments[1])) {
sources.add(entity.absolute);
}
}
}
}
void visitBehavior(SourceBehavior sourceBehavior) {
if (inputs) {
sources.addAll(sourceBehavior.inputs(environment));
} else {
sources.addAll(sourceBehavior.outputs(environment));
}
}
void visitArtifact(Artifact artifact, TargetPlatform platform, BuildMode mode) {
final String path = artifacts.getArtifactPath(artifact, platform: platform, mode: mode);
if (fs.isDirectorySync(path)) {
sources.addAll(<File>[
for (FileSystemEntity entity in fs.directory(path).listSync(recursive: true))
if (entity is File)
entity
]);
} else {
sources.add(fs.file(path));
}
}
}
abstract class Source {
const factory Source.pattern(String pattern, { bool optional }) = _PatternSource;
const factory Source.function(InputFunction function) = _FunctionSource;
const factory Source.behavior(SourceBehavior behavior) = _SourceBehavior;
const factory Source.artifact(Artifact artifact, {TargetPlatform platform,
BuildMode mode}) = _ArtifactSource;
void accept(SourceVisitor visitor);
bool get implicit;
}
abstract class SourceBehavior {
const SourceBehavior();
List<File> inputs(Environment environment);
List<File> outputs(Environment environment);
}
class _SourceBehavior implements Source {
const _SourceBehavior(this.value);
final SourceBehavior value;
@override
void accept(SourceVisitor visitor) => visitor.visitBehavior(value);
@override
bool get implicit => true;
}
class _FunctionSource implements Source {
const _FunctionSource(this.value);
final InputFunction value;
@override
void accept(SourceVisitor visitor) => visitor.visitFunction(value);
@override
bool get implicit => true;
}
class _PatternSource implements Source {
const _PatternSource(this.value, { this.optional = false });
final String value;
final bool optional;
@override
void accept(SourceVisitor visitor) => visitor.visitPattern(value, optional);
@override
bool get implicit => value.contains('*');
}
class _ArtifactSource implements Source {
const _ArtifactSource(this.artifact, { this.platform, this.mode });
final Artifact artifact;
final TargetPlatform platform;
final BuildMode mode;
@override
void accept(SourceVisitor visitor) => visitor.visitArtifact(artifact, platform, mode);
@override
bool get implicit => false;
}