1717#include " cuttlefish/host/commands/cvd/cli/commands/create.h"
1818
1919#include < errno.h>
20+ #include < fmt/core.h>
21+ #include < fmt/format.h>
2022#include < stddef.h>
2123
2224#include < algorithm>
2527#include < cstring>
2628#include < limits>
2729#include < memory>
30+ #include < optional>
2831#include < sstream>
2932#include < string>
3033#include < utility>
3134#include < vector>
3235
33- #include < fmt/core.h>
34- #include < fmt/format.h>
3536#include " absl/log/log.h"
3637#include " absl/strings/match.h"
3738#include " absl/strings/str_join.h"
39+ #include " absl/strings/str_split.h"
3840
3941#include " cuttlefish/common/libs/utils/contains.h"
4042#include " cuttlefish/common/libs/utils/environment.h"
4143#include " cuttlefish/common/libs/utils/files.h"
44+ #include " cuttlefish/common/libs/utils/users.h"
4245#include " cuttlefish/flag_parser/flag.h"
4346#include " cuttlefish/flag_parser/gflags_compat.h"
44- #include " cuttlefish/common/libs/utils/users.h"
4547#include " cuttlefish/host/commands/cvd/cli/command_request.h"
4648#include " cuttlefish/host/commands/cvd/cli/commands/command_handler.h"
4749#include " cuttlefish/host/commands/cvd/cli/commands/host_tool_target.h"
5052#include " cuttlefish/host/commands/cvd/cli/help_format.h"
5153#include " cuttlefish/host/commands/cvd/cli/selector/creation_analyzer.h"
5254#include " cuttlefish/host/commands/cvd/cli/selector/num_instances_parser.h"
55+ #include " cuttlefish/host/commands/cvd/cli/selector/selector.h"
5356#include " cuttlefish/host/commands/cvd/cli/selector/selector_common_parser.h"
5457#include " cuttlefish/host/commands/cvd/cli/selector/selector_constants.h"
5558#include " cuttlefish/host/commands/cvd/cli/types.h"
59+ #include " cuttlefish/host/commands/cvd/cli/utils.h"
5660#include " cuttlefish/host/commands/cvd/instances/cvd_persistent_data.pb.h"
5761#include " cuttlefish/host/commands/cvd/instances/instance_manager.h"
5862#include " cuttlefish/host/commands/cvd/instances/local_instance_group.h"
@@ -70,8 +74,6 @@ using selector::GroupCreationInfo;
7074constexpr char kSummaryHelpText [] =
7175 " Create a Cuttlefish instance group" ;
7276
73-
74-
7577std::string DefaultHostPath (const cvd_common::Envs& envs) {
7678 for (const auto & key : {kAndroidHostOut , kAndroidSoongHostOut , " HOME" }) {
7779 auto it = envs.find (key);
@@ -118,6 +120,18 @@ Result<CommandRequest> CreateStartCommand(const LocalInstanceGroup& group,
118120 .Build ());
119121}
120122
123+ Result<void > StartGroup (const LocalInstanceGroup& group,
124+ const std::vector<std::string>& subcmd_args,
125+ const cvd_common::Envs& envs,
126+ InstanceManager& instance_manager) {
127+ const CommandRequest start_cmd =
128+ CF_EXPECT (CreateStartCommand (group, subcmd_args, envs));
129+ std::unique_ptr<CvdCommandHandler> start_handler =
130+ NewCvdStartCommandHandler (instance_manager);
131+ CF_EXPECT (start_handler->Handle (start_cmd));
132+ return {};
133+ }
134+
121135Result<cvd_common::Envs> GetEnvs (const CommandRequest& request) {
122136 cvd_common::Envs envs = request.Env ();
123137 if (auto it = envs.find (" HOME" ); it != envs.end () && it->second .empty ()) {
@@ -236,12 +250,71 @@ std::vector<Flag> BuildSelectorFlagsForCreateHelp(
236250 };
237251}
238252
253+ bool IsDeviceRunning (const LocalInstanceGroup& group) {
254+ return std::ranges::any_of (group.Instances (), &LocalInstance::IsActive);
255+ }
256+
257+ Result<bool > MatchPaths (const LocalInstanceGroup& group, const std::string& target_host,
258+ const std::vector<std::string>& target_products) {
259+ const std::string real_group_host = CF_EXPECT (RealPath (group.HostArtifactsPath ()));
260+ const std::string real_target_host = CF_EXPECT (RealPath (target_host));
261+
262+ if (real_group_host != real_target_host) {
263+ return false ;
264+ }
265+
266+ const std::vector<std::string> group_products =
267+ absl::StrSplit (group.ProductOutPath (), ' ,' );
268+
269+ std::vector<std::string> real_group_products;
270+ for (const auto & path : group_products) {
271+ real_group_products.push_back (CF_EXPECT (RealPath (path)));
272+ }
273+
274+ std::vector<std::string> real_target_products;
275+ for (const auto & path : target_products) {
276+ real_target_products.push_back (CF_EXPECT (RealPath (path)));
277+ }
278+
279+ return real_group_products == real_target_products;
280+ }
281+
239282} // namespace
240283
241284CvdCreateCommandHandler::CvdCreateCommandHandler (
242285 InstanceManager& instance_manager)
243286 : instance_manager_(instance_manager) {}
244287
288+ Result<std::optional<LocalInstanceGroup>>
289+ CvdCreateCommandHandler::FindReusableGroup (
290+ const selector::SelectorOptions& selectors, const cvd_common::Envs& envs) {
291+ InstanceDatabase::Filter filter =
292+ selector::BuildFilterFromSelectors (selectors);
293+
294+ const std::vector<LocalInstanceGroup> groups =
295+ CF_EXPECT (instance_manager_.FindGroups (filter));
296+ if (groups.empty ()) {
297+ return std::nullopt ;
298+ }
299+ CF_EXPECT_EQ (groups.size (), 1 , " Unclear which group to reuse" );
300+
301+ auto target_host_it = envs.find (kAndroidHostOut );
302+ CF_EXPECT (target_host_it != envs.end ());
303+
304+ auto target_product_it = envs.find (kAndroidProductOut );
305+ CF_EXPECT (target_product_it != envs.end ());
306+
307+ const std::vector<std::string> target_products = ExpandProductPaths (
308+ target_product_it->second , num_instances_parser_.NumInstances ());
309+ bool match = CF_EXPECT (
310+ MatchPaths (groups[0 ], target_host_it->second , target_products));
311+ CF_EXPECT (std::move (match),
312+ " Trying to change the host paths on an existing group" );
313+ CF_EXPECT (!IsDeviceRunning (groups[0 ]),
314+ " Group is already running, try `cvd stop`" );
315+ return groups[0 ];
316+ }
317+
245318Result<void > CvdCreateCommandHandler::Handle (const CommandRequest& request) {
246319 std::vector<std::string> subcmd_args = request.SubcommandArguments ();
247320
@@ -275,23 +348,23 @@ Result<void> CvdCreateCommandHandler::Handle(const CommandRequest& request) {
275348 // CreationAnalyzer needs these to be set in the environment
276349 envs[kAndroidHostOut ] = AbsolutePath (own_flags_.host_path );
277350 envs[kAndroidProductOut ] = AbsolutePath (own_flags_.product_path );
278- auto group =
279- CF_EXPECT (CreateGroup (instance_manager_, subcmd_args, envs, request));
280351
281- group.SetAllStates (cvd::INSTANCE_STATE_STOPPED );
282- CF_EXPECT (instance_manager_.UpdateInstanceGroup (group));
352+ std::optional<LocalInstanceGroup> group;
353+ if (own_flags_.reuse ) {
354+ group = CF_EXPECT (FindReusableGroup (request.Selectors (), envs));
355+ }
356+ if (!group.has_value ()) {
357+ group = CF_EXPECT (CreateGroup (subcmd_args, envs, request));
358+ }
359+ CF_EXPECT (group.has_value ());
283360
284361 if (own_flags_.start ) {
285- CommandRequest start_cmd =
286- CF_EXPECT (CreateStartCommand (group, subcmd_args, envs));
287- std::unique_ptr<CvdCommandHandler> start_handler =
288- NewCvdStartCommandHandler (instance_manager_);
289- CF_EXPECT (start_handler->Handle (start_cmd));
362+ CF_EXPECT (StartGroup (*group, subcmd_args, envs, instance_manager_));
290363
291364 if (CF_EXPECT (IsDefaultGroup (request))) {
292365 // For backward compatibility, we add extra symlink in system wide home
293366 // when HOME is NOT overridden and selector flags are NOT given.
294- auto symlink_res = CreateSymlinks (group);
367+ auto symlink_res = CreateSymlinks (* group);
295368 if (!symlink_res.ok ()) {
296369 LOG (ERROR ) << " Failed to create symlinks for default group: "
297370 << symlink_res.error ();
@@ -303,7 +376,6 @@ Result<void> CvdCreateCommandHandler::Handle(const CommandRequest& request) {
303376}
304377
305378Result<LocalInstanceGroup> CvdCreateCommandHandler::CreateGroup (
306- InstanceManager& instance_manager,
307379 const std::vector<std::string>& subcmd_args, const cvd_common::Envs& envs,
308380 const CommandRequest& request) {
309381 GroupCreationInfo creation_info = CF_EXPECT (AnalyzeCreation ({
@@ -313,15 +385,20 @@ Result<LocalInstanceGroup> CvdCreateCommandHandler::CreateGroup(
313385 .instance_ids = num_instances_parser_.InstanceIds (),
314386 }));
315387
316- auto groups = CF_EXPECT (instance_manager .FindGroups (
388+ auto groups = CF_EXPECT (instance_manager_ .FindGroups (
317389 {.group_name = creation_info.group_creation_params .group_name }));
318390 CF_EXPECTF (groups.empty (), " Group named '{}' already exists" ,
319391 creation_info.group_creation_params .group_name );
320- return instance_manager.CreateInstanceGroup (
392+
393+ LocalInstanceGroup group = CF_EXPECT (instance_manager_.CreateInstanceGroup (
321394 std::move (creation_info.group_creation_params ),
322- std::move (creation_info.group_directories ));
323- }
395+ std::move (creation_info.group_directories )));
324396
397+ group.SetAllStates (cvd::INSTANCE_STATE_STOPPED );
398+ CF_EXPECT (instance_manager_.UpdateInstanceGroup (group));
399+
400+ return group;
401+ }
325402
326403std::string CvdCreateCommandHandler::SummaryHelp () const {
327404 return kSummaryHelpText ;
@@ -404,6 +481,7 @@ std::vector<Flag> CvdCreateCommandHandler::FlagModeFlags(
404481 own_flags_.host_path = DefaultHostPath (env);
405482 own_flags_.product_path = DefaultProductPath (env);
406483 own_flags_.start = true ;
484+ own_flags_.reuse = false ;
407485 std::vector<Flag> flags = num_instances_parser_.Flags (selector_options);
408486 flags.emplace_back (
409487 GflagsCompatFlag (" host_path" , own_flags_.host_path )
@@ -417,6 +495,11 @@ std::vector<Flag> CvdCreateCommandHandler::FlagModeFlags(
417495 " $ANDROID_PRODUCT_OUT, $HOME or the current directory." ));
418496 flags.emplace_back (GflagsCompatFlag (" start" , own_flags_.start )
419497 .Help (" Whether to start the instance group." ));
498+ flags.emplace_back (
499+ GflagsCompatFlag (" reuse" , own_flags_.reuse )
500+ .Help (" Whether to attempt reusing an existing group. Will fail if "
501+ " there are multiple matching groups, or the chosen group has a "
502+ " host tools / guest image mismatch." ));
420503 return flags;
421504}
422505
0 commit comments