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,73 @@ 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::TryReuseGroup (
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+ } else if (groups.size () == 1 ) {
299+ auto target_host_it = envs.find (kAndroidHostOut );
300+ CF_EXPECT (target_host_it != envs.end ());
301+
302+ auto target_product_it = envs.find (kAndroidProductOut );
303+ CF_EXPECT (target_product_it != envs.end ());
304+
305+ const std::vector<std::string> target_products = ExpandProductPaths (
306+ target_product_it->second , num_instances_parser_.NumInstances ());
307+ bool match = CF_EXPECT (
308+ MatchPaths (groups[0 ], target_host_it->second , target_products));
309+ CF_EXPECT (std::move (match),
310+ " Trying to change the host paths on an existing group" );
311+ CF_EXPECT (!IsDeviceRunning (groups[0 ]),
312+ " Group is already running, try `cvd stop`" );
313+ return groups[0 ];
314+ } else {
315+ return CF_ERRF (" Unclear which of {} groups to match, try `cvd clear`" ,
316+ groups.size ());
317+ }
318+ }
319+
245320Result<void > CvdCreateCommandHandler::Handle (const CommandRequest& request) {
246321 std::vector<std::string> subcmd_args = request.SubcommandArguments ();
247322
@@ -275,23 +350,23 @@ Result<void> CvdCreateCommandHandler::Handle(const CommandRequest& request) {
275350 // CreationAnalyzer needs these to be set in the environment
276351 envs[kAndroidHostOut ] = AbsolutePath (own_flags_.host_path );
277352 envs[kAndroidProductOut ] = AbsolutePath (own_flags_.product_path );
278- auto group =
279- CF_EXPECT (CreateGroup (instance_manager_, subcmd_args, envs, request));
280353
281- group.SetAllStates (cvd::INSTANCE_STATE_STOPPED );
282- CF_EXPECT (instance_manager_.UpdateInstanceGroup (group));
354+ std::optional<LocalInstanceGroup> group;
355+ if (own_flags_.reuse ) {
356+ group = CF_EXPECT (TryReuseGroup (request.Selectors (), envs));
357+ }
358+ if (!group.has_value ()) {
359+ group = CF_EXPECT (CreateGroup (subcmd_args, envs, request));
360+ }
361+ CF_EXPECT (group.has_value ());
283362
284363 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));
364+ CF_EXPECT (StartGroup (*group, subcmd_args, envs, instance_manager_));
290365
291366 if (CF_EXPECT (IsDefaultGroup (request))) {
292367 // For backward compatibility, we add extra symlink in system wide home
293368 // when HOME is NOT overridden and selector flags are NOT given.
294- auto symlink_res = CreateSymlinks (group);
369+ auto symlink_res = CreateSymlinks (* group);
295370 if (!symlink_res.ok ()) {
296371 LOG (ERROR ) << " Failed to create symlinks for default group: "
297372 << symlink_res.error ();
@@ -303,7 +378,6 @@ Result<void> CvdCreateCommandHandler::Handle(const CommandRequest& request) {
303378}
304379
305380Result<LocalInstanceGroup> CvdCreateCommandHandler::CreateGroup (
306- InstanceManager& instance_manager,
307381 const std::vector<std::string>& subcmd_args, const cvd_common::Envs& envs,
308382 const CommandRequest& request) {
309383 GroupCreationInfo creation_info = CF_EXPECT (AnalyzeCreation ({
@@ -313,15 +387,20 @@ Result<LocalInstanceGroup> CvdCreateCommandHandler::CreateGroup(
313387 .instance_ids = num_instances_parser_.InstanceIds (),
314388 }));
315389
316- auto groups = CF_EXPECT (instance_manager .FindGroups (
390+ auto groups = CF_EXPECT (instance_manager_ .FindGroups (
317391 {.group_name = creation_info.group_creation_params .group_name }));
318392 CF_EXPECTF (groups.empty (), " Group named '{}' already exists" ,
319393 creation_info.group_creation_params .group_name );
320- return instance_manager.CreateInstanceGroup (
394+
395+ LocalInstanceGroup group = CF_EXPECT (instance_manager_.CreateInstanceGroup (
321396 std::move (creation_info.group_creation_params ),
322- std::move (creation_info.group_directories ));
323- }
397+ std::move (creation_info.group_directories )));
324398
399+ group.SetAllStates (cvd::INSTANCE_STATE_STOPPED );
400+ CF_EXPECT (instance_manager_.UpdateInstanceGroup (group));
401+
402+ return group;
403+ }
325404
326405std::string CvdCreateCommandHandler::SummaryHelp () const {
327406 return kSummaryHelpText ;
@@ -404,6 +483,7 @@ std::vector<Flag> CvdCreateCommandHandler::FlagModeFlags(
404483 own_flags_.host_path = DefaultHostPath (env);
405484 own_flags_.product_path = DefaultProductPath (env);
406485 own_flags_.start = true ;
486+ own_flags_.reuse = false ;
407487 std::vector<Flag> flags = num_instances_parser_.Flags (selector_options);
408488 flags.emplace_back (
409489 GflagsCompatFlag (" host_path" , own_flags_.host_path )
@@ -417,6 +497,11 @@ std::vector<Flag> CvdCreateCommandHandler::FlagModeFlags(
417497 " $ANDROID_PRODUCT_OUT, $HOME or the current directory." ));
418498 flags.emplace_back (GflagsCompatFlag (" start" , own_flags_.start )
419499 .Help (" Whether to start the instance group." ));
500+ flags.emplace_back (
501+ GflagsCompatFlag (" reuse" , own_flags_.reuse )
502+ .Help (" Whether to attempt reusing an existing group. Will fail if "
503+ " there are multiple matching groups, or the chosen group has a "
504+ " host tools / guest image mismatch." ));
420505 return flags;
421506}
422507
0 commit comments