diff --git a/zx b/zx index a66d838..0f21ff0 100755 --- a/zx +++ b/zx @@ -144,44 +144,13 @@ run(Identifier, Args) -> State = #s{realm = Realm, name = Name, version = Version}, - NextState = #s{version = Installed} = ensure_installed(State), + NewState = #s{version = Installed} = ensure_installed(State), PackageID = {Realm, Name, Installed}, Dir = filename:join("lib", package_string(PackageID)), Meta = read_meta(Dir), Deps = maps:get(deps, Meta), - NewState = ensure_deps(NextState#s{dir = Dir, deps = Deps}), - execute(NewState). - - -% Required = [PackageID | Deps], -% Needed = scrub(Required), -% Host = {"localhost", 11411}, -% Socket = connect(Host, user), -% ok = fetch(Socket, Needed), -% ok = lists:foreach(fun install/1, Needed), -% ok = lists:foreach(fun build/1, Required), -% ok = file:set_cwd(PackageRoot), -% case maps:get(type, Meta) of -% app -> -% true = register(zx, self()), -% ok = inets:start(), -% ok = log(info, "Starting ~ts", [package_string(PackageID)]), -% PackageMod = list_to_atom(Name), -% {ok, Pid} = PackageMod:start(normal, Args), -% Mon = monitor(process, Pid), -% Shell = spawn(shell, start, []), -% ok = log(info, "Your shell is ~p, application is: ~p", [Shell, Pid]), -% State = #s{realm = Realm, -% name = Name, -% version = Version, -% pid = Pid, -% mon = Mon}, -% exec_wait(State); -% lib -> -% Message = "Lib ~ts is available on the system, but is not a standalone app.", -% ok = log(info, Message, [package_string(PackageID)]), -% halt(0) -% end. + ok = ensure_deps(Deps), + execute(NewState#s{dir = Dir, deps = Deps}). @@ -348,10 +317,10 @@ ensure_installed(State = #s{realm = Realm, name = Name, version = Version}) -> | exact | {ok, Installed :: version()}. %% @private -%% Resolve the provided PackageID to the latest matching installed package directory version -%% if one exists, returning a value that indicates whether an exact match was found (in the -%% case of a full version input), a version matching a partial version input was found, or no -%% match was found at all. +%% Resolve the provided PackageID to the latest matching installed package directory +%% version if one exists, returning a value that indicates whether an exact match was +%% found (in the case of a full version input), a version matching a partial version +%% input was found, or no match was found at all. resolve_installed_version(PackageID) -> PackageString = package_string(PackageID), @@ -371,35 +340,52 @@ resolve_installed_version(PackageID) -> end. -ensure_deps(State = #s{realm = Realm, deps = Deps, socket = MaybeSocket}) -> +ensure_deps(Deps) -> case scrub(Deps) of [] -> - State; + ok; Needed -> - Sorted = lists:sort(Needed), - Partition = - fun(D = {R, _, _}, M) -> - maps:update_with(R, fun(Ds) -> [D | Ds] end, [D], M) + Partitioned = partition_by_realm(Needed), + EnsureDeps = + fun({Realm, Packages}) -> + Socket = connect_user(Realm), + ok = ensure_deps(Socket, Realm, Packages), + ok = disconnect(Socket), + log(info, "Disconnecting from realm: ~ts", [Realm]), end, - Partitioned = lists:foldl(Partition, #{}, Needed), + lists:foreach(EnsureDeps, Partitioned) + end. --spec ensure_dep(package_id()) -> ok | no_return(). +partition_by_realm(PackageIDs) -> + Sorted = lists:sort(Needed), + PartitionMap = lists:foldl(fun partition_by_realm/2, #{}, Needed), + maps:to_list(PartitionMap). + + +partition_by_realm({R, P, V}, M) -> + maps:update_with(R, fun(Ps) -> [{P, V} | Ps] end, [{P, V}], M). + + +ensure_deps(_, _, []) -> + ok; +ensure_deps(Socket, Realm, [{Name, Version} | Rest]) -> + ok = ensure_dep(Socket, {Realm, Name, Version}), + ensure_deps(Socket, Realm, Rest). + + +-spec ensure_dep(gen_tcp:socket(), package_id()) -> ok | no_return(). %% @private %% Given an PackageID as an argument, check whether its package file exists in the %% system cache, and if not download it. Should return `ok' whenever the file is %% sourced, but exit with an error if it cannot locate or acquire the package. -ensure_dep(PackageID) -> +ensure_dep(Socket, PackageID) -> ZrpFile = filename:join("zrp", namify_zrp(PackageID)), ok = case filelib:is_regular(ZrpFile) of - true -> - ok; - false -> - PackageString = package_string(PackageID), - log(error, "Would fetch ~ts now, but not implemented", [PackageString]), - halt(0) + true -> ok; + false -> fetch(Socket, PackageID) end, install(PackageID). @@ -576,10 +562,9 @@ verup(_) -> usage_exit(22). run_local(Args) -> Meta = read_meta(), - {package_id, PackageID} = lists:keyfind(package_id, 1, Meta), - {Realm, Name, Version} = PackageID, - {type, Type} = lists:keyfind(type, 1, Meta), - {deps, Deps} = lists:keyfind(deps, 1, Meta), + {Realm, Name, Version} = maps:get(package_id, Meta), + Type = maps:get(type, Meta), + Deps = maps:get(deps, Meta), ok = build(), {ok, Dir} = file:get_cwd(), ok = file:set_cwd(zomp_dir()), @@ -589,12 +574,8 @@ run_local(Args) -> type = Type, deps = Deps, dir = Dir}, - NewState = ensure_deps(State), - execute(State). - - -ensure_deps(Deps, State) -> - + ok = ensure_deps(Deps), + ok = file:set_cwd(Dir), execute(State). @@ -613,23 +594,14 @@ execute(State = #s{type = app}) -> pid = Pid, mon = Mon}, exec_wait(State); -execute(State = #s{type = lib}) -> +execute(State = #s{type = lib, realm = Realm, name = Name, version = Version}) -> Message = "Lib ~ts is available on the system, but is not a standalone app", - ok = log(info, Message, [package_string(PackageID)]), + PackageString = package_string({Realm, Name, Version}), + ok = log(info, Message, [PackageString]), halt(0). -% Needed = scrub(Deps), -% Host = {"localhost", 11411}, -% Socket = connect(Host, user), -% ok = fetch(Socket, Needed), -% ok = lists:foreach(fun install/1, Needed), -% ok = lists:foreach(fun build/1, Deps), -% ok = file:set_cwd(ProjectRoot), - - - %%% Package generation -spec package(TargetDir) -> no_return() @@ -1443,53 +1415,45 @@ verify(Data, Signature, PubKey) -> end. --spec fetch(gen_tcp:socket(), [package_id()]) -> ok. +-spec fetch(gen_tcp:socket(), package_id()) -> ok. %% @private -%% Download a list of deps to the local package cache. +%% Download a package to the local cache. -fetch(Socket, Needed) -> - Namified = lists:map(fun namify_zrp/1, Needed), - {Have, Lack} = lists:partition(fun filelib:is_regular/1, Namified), - ok = lists:foreach(fun(A) -> log(info, "Have ~ts", [A]) end, Have), - ok = lists:foreach(fun(A) -> log(info, "Lack ~ts", [A]) end, Lack), - ok = send(Socket, "Would be sending fetch requests now."), - log(info, "Done fake fetching"). +fetch(Socket, PackageID) -> + ok = send(Socket, {fetch, PackageID}), + ok = await_zrp(Socket, PackageID), + ok = receive_zrp(Socket, PackageID), + log(info, "Fetched ~ts", [package_string(PackageID)]). -% Grouped = group_by_realm(Needed), -% Realms = [R || {R, _} <- Grouped], -% Sockets = connect(Realms), -% fetch(Sockets, Groups). -% -% -%-spec group_by_realm(AppIDs) -> GroupedAppIDs -% when AppIDs :: [app_id()], -% GroupedAppIDs :: [{realm(), [app_id()]}]. -%%% @private -%%% Group apps by realm. -% -%group_by_realm(AppIDs) -> -% Group = -% fun(AppID = {Realm, _, _}, Groups) -> -% case lists:keyfind(Realm, Groups) of -% {Realm, Members} -> -% lists:keystore(Realm, 1, Groups, {Realm, [AppID | Members]}); -% false -> -% lists:keystore(Realm, 1, Groups, {Realm, [AppID]}) -% end -% end, -% lists:foldl(Group, AppIDs). -% -% -% ZrpFile = namify_zrp(AppID), -% case filelib:is_regular(filename:join("zrp", ZrpFile)) of -% true -> -% log(info, "Found in cache: ~ts", [ZrpFile]); -% false -> -% log(info, "Would download: ~ts", [ZrpFile]) -% end. +await_zrp(Socket, PackageID) -> + receive + {tcp, Socket, Bin} -> + case binary_to_term(Bin, [safe]) of + sending -> + ok; + Error = {error, Reason} -> + PackageString = package_string(PackageID), + Message = "Error receiving package ~ts: ~tp", + ok = log(info, Message, [PackageString, Reason]), + Error + after 60000 -> + {error, timeout} + end. +receive_zrp(Socket, PackageID) -> + receive + {tcp, Socket, Bin} -> + ZrpPath = filename:join("zrp", namify_zrp(PackageID)), + ok = file:write_file(ZrpPath, Bin), + ok = send(Socket, ok), + log(info, "Wrote ~ts", [ZrpPath]) + after 60000 -> + ok = log(error, "Timeout in socket receive for ~tp", [PackageID]), + {error, timeout} + end. + %%% Utility functions