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>
31+ #include < ranges>
2832#include < sstream>
2933#include < string>
3034#include < utility>
3135#include < vector>
3236
33- #include < fmt/core.h>
34- #include < fmt/format.h>
3537#include " absl/log/log.h"
3638#include " absl/strings/match.h"
3739#include " absl/strings/str_join.h"
40+ #include " absl/strings/str_split.h"
3841
3942#include " cuttlefish/common/libs/utils/contains.h"
4043#include " cuttlefish/common/libs/utils/environment.h"
4144#include " cuttlefish/common/libs/utils/files.h"
45+ #include " cuttlefish/common/libs/utils/users.h"
4246#include " cuttlefish/flag_parser/flag.h"
4347#include " cuttlefish/flag_parser/gflags_compat.h"
44- #include " cuttlefish/common/libs/utils/users.h"
4548#include " cuttlefish/host/commands/cvd/cli/command_request.h"
4649#include " cuttlefish/host/commands/cvd/cli/commands/command_handler.h"
4750#include " cuttlefish/host/commands/cvd/cli/commands/host_tool_target.h"
5053#include " cuttlefish/host/commands/cvd/cli/help_format.h"
5154#include " cuttlefish/host/commands/cvd/cli/selector/creation_analyzer.h"
5255#include " cuttlefish/host/commands/cvd/cli/selector/num_instances_parser.h"
56+ #include " cuttlefish/host/commands/cvd/cli/selector/selector.h"
5357#include " cuttlefish/host/commands/cvd/cli/selector/selector_common_parser.h"
5458#include " cuttlefish/host/commands/cvd/cli/selector/selector_constants.h"
5559#include " cuttlefish/host/commands/cvd/cli/types.h"
60+ #include " cuttlefish/host/commands/cvd/cli/utils.h"
5661#include " cuttlefish/host/commands/cvd/instances/cvd_persistent_data.pb.h"
5762#include " cuttlefish/host/commands/cvd/instances/instance_manager.h"
5863#include " cuttlefish/host/commands/cvd/instances/local_instance_group.h"
@@ -70,8 +75,6 @@ using selector::GroupCreationInfo;
7075constexpr char kSummaryHelpText [] =
7176 " Create a Cuttlefish instance group" ;
7277
73-
74-
7578std::string DefaultHostPath (const cvd_common::Envs& envs) {
7679 for (const auto & key : {kAndroidHostOut , kAndroidSoongHostOut , " HOME" }) {
7780 auto it = envs.find (key);
@@ -118,6 +121,18 @@ Result<CommandRequest> CreateStartCommand(const LocalInstanceGroup& group,
118121 .Build ());
119122}
120123
124+ Result<void > StartGroup (const LocalInstanceGroup& group,
125+ const std::vector<std::string>& subcmd_args,
126+ const cvd_common::Envs& envs,
127+ InstanceManager& instance_manager) {
128+ const CommandRequest start_cmd =
129+ CF_EXPECT (CreateStartCommand (group, subcmd_args, envs));
130+ std::unique_ptr<CvdCommandHandler> start_handler =
131+ NewCvdStartCommandHandler (instance_manager);
132+ CF_EXPECT (start_handler->Handle (start_cmd));
133+ return {};
134+ }
135+
121136Result<cvd_common::Envs> GetEnvs (const CommandRequest& request) {
122137 cvd_common::Envs envs = request.Env ();
123138 if (auto it = envs.find (" HOME" ); it != envs.end () && it->second .empty ()) {
@@ -236,12 +251,73 @@ std::vector<Flag> BuildSelectorFlagsForCreateHelp(
236251 };
237252}
238253
254+ bool IsDeviceRunning (const LocalInstanceGroup& group) {
255+ return std::ranges::any_of (group.Instances (), &LocalInstance::IsActive);
256+ }
257+
258+ Result<bool > MatchPaths (const LocalInstanceGroup& group, const std::string& target_host,
259+ const std::vector<std::string>& target_products) {
260+ const std::string real_group_host = CF_EXPECT (RealPath (group.HostArtifactsPath ()));
261+ const std::string real_target_host = CF_EXPECT (RealPath (target_host));
262+
263+ if (real_group_host != real_target_host) {
264+ return false ;
265+ }
266+
267+ const std::vector<std::string> group_products =
268+ absl::StrSplit (group.ProductOutPath (), ' ,' );
269+
270+ std::vector<std::string> real_group_products;
271+ for (const auto & path : group_products) {
272+ real_group_products.push_back (CF_EXPECT (RealPath (path)));
273+ }
274+
275+ std::vector<std::string> real_target_products;
276+ for (const auto & path : target_products) {
277+ real_target_products.push_back (CF_EXPECT (RealPath (path)));
278+ }
279+
280+ return real_group_products == real_target_products;
281+ }
282+
239283} // namespace
240284
241285CvdCreateCommandHandler::CvdCreateCommandHandler (
242286 InstanceManager& instance_manager)
243287 : instance_manager_(instance_manager) {}
244288
289+ Result<std::optional<LocalInstanceGroup>>
290+ CvdCreateCommandHandler::TryReuseGroup (
291+ const selector::SelectorOptions& selectors, const cvd_common::Envs& envs) {
292+ InstanceDatabase::Filter filter =
293+ selector::BuildFilterFromSelectors (selectors);
294+
295+ const std::vector<LocalInstanceGroup> groups =
296+ CF_EXPECT (instance_manager_.FindGroups (filter));
297+ if (groups.empty ()) {
298+ return std::nullopt ;
299+ } else if (groups.size () == 1 ) {
300+ auto target_host_it = envs.find (kAndroidHostOut );
301+ CF_EXPECT (target_host_it != envs.end ());
302+
303+ auto target_product_it = envs.find (kAndroidProductOut );
304+ CF_EXPECT (target_product_it != envs.end ());
305+
306+ const std::vector<std::string> target_products = ExpandProductPaths (
307+ target_product_it->second , num_instances_parser_.NumInstances ());
308+ bool match = CF_EXPECT (
309+ MatchPaths (groups[0 ], target_host_it->second , target_products));
310+ CF_EXPECT (std::move (match),
311+ " Trying to change the host paths on an existing group" );
312+ CF_EXPECT (!IsDeviceRunning (groups[0 ]),
313+ " Group is already running, try `cvd stop`" );
314+ return groups[0 ];
315+ } else {
316+ return CF_ERRF (" Unclear which of {} groups to match, try `cvd clear`" ,
317+ groups.size ());
318+ }
319+ }
320+
245321Result<void > CvdCreateCommandHandler::Handle (const CommandRequest& request) {
246322 std::vector<std::string> subcmd_args = request.SubcommandArguments ();
247323
@@ -275,23 +351,23 @@ Result<void> CvdCreateCommandHandler::Handle(const CommandRequest& request) {
275351 // CreationAnalyzer needs these to be set in the environment
276352 envs[kAndroidHostOut ] = AbsolutePath (own_flags_.host_path );
277353 envs[kAndroidProductOut ] = AbsolutePath (own_flags_.product_path );
278- auto group =
279- CF_EXPECT (CreateGroup (instance_manager_, subcmd_args, envs, request));
280354
281- group.SetAllStates (cvd::INSTANCE_STATE_STOPPED );
282- CF_EXPECT (instance_manager_.UpdateInstanceGroup (group));
355+ std::optional<LocalInstanceGroup> group;
356+ if (own_flags_.reuse ) {
357+ group = CF_EXPECT (TryReuseGroup (request.Selectors (), envs));
358+ }
359+ if (!group.has_value ()) {
360+ group = CF_EXPECT (CreateGroup (subcmd_args, envs, request));
361+ }
362+ CF_EXPECT (group.has_value ());
283363
284364 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));
365+ CF_EXPECT (StartGroup (*group, subcmd_args, envs, instance_manager_));
290366
291367 if (CF_EXPECT (IsDefaultGroup (request))) {
292368 // For backward compatibility, we add extra symlink in system wide home
293369 // when HOME is NOT overridden and selector flags are NOT given.
294- auto symlink_res = CreateSymlinks (group);
370+ auto symlink_res = CreateSymlinks (* group);
295371 if (!symlink_res.ok ()) {
296372 LOG (ERROR ) << " Failed to create symlinks for default group: "
297373 << symlink_res.error ();
@@ -303,7 +379,6 @@ Result<void> CvdCreateCommandHandler::Handle(const CommandRequest& request) {
303379}
304380
305381Result<LocalInstanceGroup> CvdCreateCommandHandler::CreateGroup (
306- InstanceManager& instance_manager,
307382 const std::vector<std::string>& subcmd_args, const cvd_common::Envs& envs,
308383 const CommandRequest& request) {
309384 GroupCreationInfo creation_info = CF_EXPECT (AnalyzeCreation ({
@@ -313,15 +388,20 @@ Result<LocalInstanceGroup> CvdCreateCommandHandler::CreateGroup(
313388 .instance_ids = num_instances_parser_.InstanceIds (),
314389 }));
315390
316- auto groups = CF_EXPECT (instance_manager .FindGroups (
391+ auto groups = CF_EXPECT (instance_manager_ .FindGroups (
317392 {.group_name = creation_info.group_creation_params .group_name }));
318393 CF_EXPECTF (groups.empty (), " Group named '{}' already exists" ,
319394 creation_info.group_creation_params .group_name );
320- return instance_manager.CreateInstanceGroup (
395+
396+ LocalInstanceGroup group = CF_EXPECT (instance_manager_.CreateInstanceGroup (
321397 std::move (creation_info.group_creation_params ),
322- std::move (creation_info.group_directories ));
323- }
398+ std::move (creation_info.group_directories )));
324399
400+ group.SetAllStates (cvd::INSTANCE_STATE_STOPPED );
401+ CF_EXPECT (instance_manager_.UpdateInstanceGroup (group));
402+
403+ return group;
404+ }
325405
326406std::string CvdCreateCommandHandler::SummaryHelp () const {
327407 return kSummaryHelpText ;
@@ -404,6 +484,7 @@ std::vector<Flag> CvdCreateCommandHandler::FlagModeFlags(
404484 own_flags_.host_path = DefaultHostPath (env);
405485 own_flags_.product_path = DefaultProductPath (env);
406486 own_flags_.start = true ;
487+ own_flags_.reuse = false ;
407488 std::vector<Flag> flags = num_instances_parser_.Flags (selector_options);
408489 flags.emplace_back (
409490 GflagsCompatFlag (" host_path" , own_flags_.host_path )
@@ -417,6 +498,11 @@ std::vector<Flag> CvdCreateCommandHandler::FlagModeFlags(
417498 " $ANDROID_PRODUCT_OUT, $HOME or the current directory." ));
418499 flags.emplace_back (GflagsCompatFlag (" start" , own_flags_.start )
419500 .Help (" Whether to start the instance group." ));
501+ flags.emplace_back (
502+ GflagsCompatFlag (" reuse" , own_flags_.reuse )
503+ .Help (" Whether to attempt reusing an existing group. Will fail if "
504+ " there are multiple matching groups, or the chosen group has a "
505+ " host tools / guest image mismatch." ));
420506 return flags;
421507}
422508
0 commit comments