Skip to content

Commit b625bf4

Browse files
committed
adds cmd to pull header files from google/mediapipe
1 parent 3046f3e commit b625bf4

File tree

8 files changed

+309
-0
lines changed

8 files changed

+309
-0
lines changed

packages/build_cmd/.gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# https://dart.dev/guides/libraries/private-files
2+
# Created by `dart pub`
3+
.dart_tool/

packages/build_cmd/CHANGELOG.md

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
## 1.0.0
2+
3+
- Initial version.

packages/build_cmd/README.md

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
A sample command-line application with an entrypoint in `bin/`, library code
2+
in `lib/`, and example unit test in `test/`.
+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# This file configures the static analysis results for your project (errors,
2+
# warnings, and lints).
3+
#
4+
# This enables the 'recommended' set of lints from `package:lints`.
5+
# This set helps identify many issues that may lead to problems when running
6+
# or consuming Dart code, and enforces writing Dart using a single, idiomatic
7+
# style and format.
8+
#
9+
# If you want a smaller set of lints you can change this to specify
10+
# 'package:lints/core.yaml'. These are just the most critical lints
11+
# (the recommended set includes the core lints).
12+
# The core lints are also what is used by pub.dev for scoring packages.
13+
14+
include: package:lints/recommended.yaml
15+
16+
# Uncomment the following section to specify additional rules.
17+
18+
# linter:
19+
# rules:
20+
# - camel_case_types
21+
22+
# analyzer:
23+
# exclude:
24+
# - path/to/excluded/files/**
25+
26+
# For more information about the core and recommended set of lints, see
27+
# https://dart.dev/go/core-lints
28+
29+
# For additional information about configuring this file, see
30+
# https://dart.dev/guides/language/analysis-options

packages/build_cmd/bin/main.dart

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import 'package:args/command_runner.dart';
2+
import 'package:build_cmd/sync_headers.dart';
3+
4+
final runner = CommandRunner(
5+
'build',
6+
'Performs build operations for google/flutter-mediapipe that '
7+
'depend on contents in this repository',
8+
)..addCommand(SyncHeadersCommand());
9+
10+
void main(List<String> arguments) => runner.run(arguments);
+78
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import 'dart:io' as io;
2+
import 'package:args/args.dart';
3+
import 'package:args/command_runner.dart';
4+
import 'package:path/path.dart' as path;
5+
import 'package:io/ansi.dart';
6+
7+
mixin RepoFinderMixin on Command {
8+
void addSourceOption(ArgParser argParser) {
9+
argParser.addOption(
10+
'source',
11+
abbr: 's',
12+
help: 'The location of google/mediapipe. Defaults to being '
13+
'adjacent to google/flutter-mediapipe.',
14+
);
15+
}
16+
17+
/// Looks upward for the root of the `google/mediapipe` repository. This assumes
18+
/// the `dart build` command is executed from within said repository. If it is
19+
/// not executed from within, then this searching algorithm will reach the root
20+
/// of the file system, log the error, and exit.
21+
io.Directory findFlutterMediaPipeRoot() {
22+
io.Directory dir = io.Directory(path.current);
23+
while (true) {
24+
if (_isFlutterMediaPipeRoot(dir)) {
25+
return dir;
26+
}
27+
dir = dir.parent;
28+
if (dir.parent.path == dir.path) {
29+
io.stderr.writeln(
30+
wrapWith(
31+
'Failed to find google/mediapipe root directory. '
32+
'Did you execute this command from within the repository?',
33+
[red],
34+
),
35+
);
36+
io.exit(1);
37+
}
38+
}
39+
}
40+
41+
/// Finds the `google/mediapipe` checkout where artifacts built in this
42+
/// repository should be sourced. By default, this command assumes the two
43+
/// repositories are siblings on the file system, but the `--origin` flag
44+
/// allows for this assumption to be overridden.
45+
io.Directory findMediaPipeRoot(
46+
io.Directory flutterMediaPipeDir,
47+
String? origin,
48+
) {
49+
final flutterMediaPipeDirectory = io.Directory(
50+
origin ??
51+
path.joinAll([
52+
flutterMediaPipeDir.parent.absolute.path,
53+
'mediapipe',
54+
]),
55+
);
56+
57+
if (!flutterMediaPipeDirectory.existsSync()) {
58+
io.stderr.writeln(
59+
'Could not find ${flutterMediaPipeDirectory.absolute.path}. '
60+
'Folder does not exist.',
61+
);
62+
io.exit(1);
63+
}
64+
return flutterMediaPipeDirectory;
65+
}
66+
}
67+
68+
/// Looks for the sentinel file of this repository's root directory. This allows
69+
/// the `dart build` command to be run from various locations within the
70+
/// `google/mediapipe` repository and still correctly set paths for all of its
71+
/// operations.
72+
bool _isFlutterMediaPipeRoot(io.Directory dir) {
73+
return io.File(
74+
path.joinAll(
75+
[dir.absolute.path, '.flutter-mediapipe-root'],
76+
),
77+
).existsSync();
78+
}
+163
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
import 'dart:convert';
2+
import 'dart:io' as io;
3+
import 'package:args/command_runner.dart';
4+
import 'package:build_cmd/repo_finder.dart';
5+
import 'package:io/ansi.dart';
6+
import 'package:path/path.dart' as path;
7+
import 'package:process/process.dart';
8+
9+
/// Relative header paths (in both repositories)
10+
final containers = 'mediapipe/tasks/c/components/containers';
11+
final processors = 'mediapipe/tasks/c/components/processors';
12+
final core = 'mediapipe/tasks/c/core';
13+
final tc = 'mediapipe/tasks/c/text/text_classifier';
14+
15+
/// google/flutter-mediapipe package paths
16+
final corePackage = 'packages/mediapipe-core/third_party';
17+
final textPackage = 'packages/mediapipe-task-text/third_party';
18+
19+
/// First string is its relative location in both repositories,
20+
/// Second string is its package location in `google/flutter-mediapipe`,
21+
/// Third string is the file name
22+
/// Fourth param is an optional function to modify the file
23+
List<(String, String, String, Function(io.File)?)> headerPaths = [
24+
(containers, corePackage, 'category.h', null),
25+
(containers, corePackage, 'classification_result.h', null),
26+
(core, corePackage, 'base_options.h', null),
27+
(processors, corePackage, 'classifier_options.h', null),
28+
(tc, textPackage, 'text_classifier.h', relativeIncludes),
29+
];
30+
31+
class SyncHeadersCommand extends Command with RepoFinderMixin {
32+
@override
33+
String description = 'Syncs header files to google/flutter-mediapipe';
34+
@override
35+
String name = 'headers';
36+
37+
SyncHeadersCommand() {
38+
argParser.addFlag(
39+
'overwrite',
40+
abbr: 'o',
41+
defaultsTo: true,
42+
help: 'If true, will overwrite existing header files '
43+
'at destination locations.',
44+
);
45+
addSourceOption(argParser);
46+
}
47+
48+
@override
49+
Future<void> run() async {
50+
final io.Directory flutterMediaPipeDirectory = findFlutterMediaPipeRoot();
51+
final io.Directory mediaPipeDirectory = findMediaPipeRoot(
52+
flutterMediaPipeDirectory,
53+
argResults!['source'],
54+
);
55+
56+
final config = Options(
57+
allowOverwrite: argResults!['overwrite'],
58+
mediaPipeDir: mediaPipeDirectory,
59+
flutterMediaPipeDir: flutterMediaPipeDirectory,
60+
);
61+
62+
await copyHeaders(config);
63+
}
64+
65+
Future<void> copyHeaders(Options config) async {
66+
final mgr = LocalProcessManager();
67+
for (final tup in headerPaths) {
68+
final headerFile = io.File(path.joinAll(
69+
[config.mediaPipeDir.absolute.path, tup.$1, tup.$3],
70+
));
71+
if (!headerFile.existsSync()) {
72+
io.stderr.writeln(
73+
'Expected to find ${headerFile.path}, but '
74+
'file does not exist.',
75+
);
76+
io.exit(1);
77+
}
78+
final destinationPath = path.joinAll(
79+
[config.flutterMediaPipeDir.absolute.path, tup.$2, tup.$1, tup.$3],
80+
);
81+
final destinationFile = io.File(destinationPath);
82+
if (destinationFile.existsSync() && !config.allowOverwrite) {
83+
io.stdout.writeln(
84+
'Warning: Not overwriting existing file at $destinationPath. '
85+
'Skipping ${tup.$3}.',
86+
);
87+
continue;
88+
}
89+
90+
ensureFolders(io.File(destinationPath));
91+
92+
final process = await mgr.start([
93+
'cp',
94+
headerFile.path,
95+
destinationPath,
96+
], runInShell: true);
97+
int processExitCode = await process.exitCode;
98+
if (processExitCode != 0) {
99+
final processStdErr = utf8.decoder.convert(
100+
(await process.stderr.toList())
101+
.fold<List<int>>([], (arr, el) => arr..addAll(el)));
102+
io.stderr.write(wrapWith(processStdErr, [red]));
103+
104+
final processStdOut = utf8.decoder.convert(
105+
(await process.stdout.toList())
106+
.fold<List<int>>([], (arr, el) => arr..addAll(el)));
107+
io.stderr.write(wrapWith(processStdOut, [red]));
108+
io.exit(processExitCode);
109+
} else {
110+
io.stderr.writeln(wrapWith('Copied ${tup.$3}', [green]));
111+
}
112+
113+
// Call the final modification function, if supplied
114+
if (tup.$4 != null) {
115+
tup.$4!.call(destinationFile);
116+
}
117+
}
118+
}
119+
120+
/// Builds any missing folders between the file and the root of the repository
121+
void ensureFolders(io.File file) {
122+
io.Directory parent = file.parent;
123+
List<io.Directory> dirsToCreate = [];
124+
while (!parent.existsSync()) {
125+
dirsToCreate.add(parent);
126+
parent = parent.parent;
127+
}
128+
for (io.Directory dir in dirsToCreate.reversed) {
129+
dir.createSync();
130+
}
131+
}
132+
}
133+
134+
class Options {
135+
const Options({
136+
required this.allowOverwrite,
137+
required this.mediaPipeDir,
138+
required this.flutterMediaPipeDir,
139+
});
140+
141+
final bool allowOverwrite;
142+
final io.Directory mediaPipeDir;
143+
final io.Directory flutterMediaPipeDir;
144+
}
145+
146+
void relativeIncludes(io.File textClassifierHeader) {
147+
assert(textClassifierHeader.path.endsWith('text_classifier.h'));
148+
String contents = textClassifierHeader.readAsStringSync();
149+
150+
Map<String, String> rewrites = {
151+
'mediapipe/tasks/c/components/containers/classification_result.h':
152+
'../../../../../../../mediapipe-core/third_party/mediapipe/tasks/c/components/containers/classification_result.h',
153+
'mediapipe/tasks/c/components/processors/classifier_options.h':
154+
'../../../../../../../mediapipe-core/third_party/mediapipe/tasks/c/components/processors/classifier_options.h',
155+
'mediapipe/tasks/c/core/base_options.h':
156+
'../../../../../../../mediapipe-core/third_party/mediapipe/tasks/c/core/base_options.h',
157+
};
158+
159+
for (final rewrite in rewrites.entries) {
160+
contents = contents.replaceAll(rewrite.key, rewrite.value);
161+
}
162+
textClassifierHeader.writeAsStringSync(contents);
163+
}

packages/build_cmd/pubspec.yaml

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
name: build
2+
description: Performs build operations for google/flutter-mediapipe that depend
3+
on contents in this repository.
4+
version: 1.0.0
5+
# repository: https://github.com/my_org/my_repo
6+
environment:
7+
sdk: ^3.2.0-162.0.dev
8+
9+
# Add regular dependencies here.
10+
dependencies:
11+
args: ^2.4.2
12+
io: ^1.0.4
13+
logging: ^1.2.0
14+
path: ^1.8.0
15+
process: ^5.0.0
16+
17+
dev_dependencies:
18+
ffigen: ^9.0.1
19+
lints: ^2.1.0
20+
test: ^1.24.0

0 commit comments

Comments
 (0)