236 lines
7.1 KiB
Ada
236 lines
7.1 KiB
Ada
with Ada.Characters.Latin_1;
|
|
with Ada.Strings.Unbounded;
|
|
with Ada.Text_IO;
|
|
|
|
with GNAT.Expect;
|
|
with GNAT.OS_Lib;
|
|
|
|
package body Alire.OS_Lib is
|
|
|
|
use GNAT.OS_Lib;
|
|
|
|
--------------------
|
|
-- Locate_In_Path --
|
|
--------------------
|
|
|
|
function Locate_In_Path (Name : String) return String is
|
|
Target : String_Access := Locate_Exec_On_Path (Name);
|
|
begin
|
|
if Target /= null then
|
|
return Result : constant String := Target.all do
|
|
Free (Target);
|
|
end return;
|
|
else
|
|
raise Program_Error with "Could not locate " & Name & " in $PATH";
|
|
end if;
|
|
end Locate_In_Path;
|
|
|
|
-------------------------
|
|
-- Spawn_With_Progress --
|
|
-------------------------
|
|
|
|
function Spawn_With_Progress (Command : String;
|
|
Arguments : String) return Integer
|
|
is
|
|
use Ada.Strings.Unbounded;
|
|
use Ada.Text_IO;
|
|
use GNAT.Expect;
|
|
|
|
Simple_Command : constant String := Ada.Directories.Simple_Name (Command);
|
|
|
|
--------------
|
|
-- Sanitize --
|
|
--------------
|
|
|
|
function Sanitize (S : String) return String is -- Remove CR y LFs
|
|
begin
|
|
return Result : String := S do
|
|
for I in Result'Range loop
|
|
if Result (I) = Ada.Characters.Latin_1.CR then
|
|
Result (I) := ' ';
|
|
elsif Result (I) = Ada.Characters.Latin_1.LF then
|
|
Result (I) := ' ';
|
|
end if;
|
|
end loop;
|
|
end return;
|
|
end Sanitize;
|
|
|
|
Indicator : constant String := "/-\|/-\|";
|
|
type Indicator_Mod is mod Indicator'Length;
|
|
Pos : Indicator_Mod := 0;
|
|
|
|
Pid : Process_Descriptor;
|
|
|
|
Match : Expect_Match;
|
|
Last_Line : Unbounded_String;
|
|
Max_Len : Natural := 0;
|
|
begin
|
|
Non_Blocking_Spawn
|
|
(Pid,
|
|
Command,
|
|
Argument_String_To_List (Arguments).all,
|
|
Err_To_Out => True);
|
|
|
|
loop
|
|
begin
|
|
Expect (Pid, Match,
|
|
"([ \t\S]+)[ \n\r\f\v]", -- works for \n and \r in output (git vs gprbuild)
|
|
Timeout => 200);
|
|
|
|
if Match >= 0 then
|
|
Last_Line := To_Unbounded_String (Sanitize (Expect_Out_Match (Pid)));
|
|
end if;
|
|
|
|
declare
|
|
Progress : constant String :=
|
|
Ada.Characters.Latin_1.CR &
|
|
Simple_Command & ": " &
|
|
Indicator (Integer (Pos) + 1) & " " &
|
|
To_String (Last_Line);
|
|
begin
|
|
Max_Len := Natural'Max (Max_Len, Progress'Length);
|
|
Put (Progress &
|
|
String'(1 .. Max_Len - Progress'Length => ' ')); -- Wipe remainder of old lines
|
|
Pos := Pos + 1;
|
|
end;
|
|
|
|
exception
|
|
when Process_Died =>
|
|
Log ("Spawned process died", Debug);
|
|
exit;
|
|
end;
|
|
end loop;
|
|
|
|
declare
|
|
Line : constant String := Ada.Characters.Latin_1.CR & Simple_Command & " completed";
|
|
begin
|
|
Max_Len := Natural'Max (Max_Len, Simple_Command'length + 2); -- If there weren't any output
|
|
Put_Line (Line & String'(1 .. Max_Len - Line'Length => ' '));
|
|
end;
|
|
|
|
return Code : Integer do
|
|
Close (Pid, Code);
|
|
end return;
|
|
end Spawn_With_Progress;
|
|
|
|
-----------
|
|
-- Spawn --
|
|
-----------
|
|
-- FIXME: memory leaks
|
|
function Spawn (Command : String;
|
|
Arguments : String := "";
|
|
Understands_Verbose : Boolean := False;
|
|
Force_Quiet : Boolean := False) return Integer
|
|
is
|
|
Extra : constant String := (if Understands_Verbose then "-v " else "");
|
|
File : File_Descriptor;
|
|
Name : String_Access;
|
|
Ok : Boolean;
|
|
begin
|
|
if Simple_Logging.Level = Debug then
|
|
Log ("Spawning: " & Command & " " & Extra & Arguments, Debug);
|
|
else
|
|
Log ("Spawning: " & Command & " " & Arguments, Debug);
|
|
end if;
|
|
|
|
if (Force_Quiet and then Log_Level /= Debug) or else Log_Level in Always | Error | Warning then
|
|
Create_Temp_Output_File (File, Name);
|
|
return Code : Integer do
|
|
Spawn
|
|
(Locate_In_Path (Command),
|
|
Argument_String_To_List (Arguments).all,
|
|
File,
|
|
Code,
|
|
Err_To_Out => False);
|
|
Delete_File (Name.all, Ok);
|
|
if not Ok then
|
|
Log ("Failed to delete tmp file: " & Name.all, Warning);
|
|
end if;
|
|
Free (Name);
|
|
end return;
|
|
elsif Log_Level = Info then
|
|
return Spawn_With_Progress (Command, Arguments);
|
|
elsif Log_Level = Detail then -- All lines, without -v
|
|
return
|
|
(Spawn (Locate_In_Path (Command),
|
|
Argument_String_To_List (Arguments).all));
|
|
else -- Debug: all lines plus -v in commands
|
|
return
|
|
(Spawn (Locate_In_Path (Command),
|
|
Argument_String_To_List (Extra & Arguments).all));
|
|
end if;
|
|
end Spawn;
|
|
|
|
-----------
|
|
-- Spawn --
|
|
-----------
|
|
|
|
procedure Spawn (Command : String;
|
|
Arguments : String := "";
|
|
Understands_Verbose : Boolean := False;
|
|
Force_Quiet : Boolean := False)
|
|
is
|
|
Code : constant Integer := Spawn (Command, Arguments, Understands_Verbose, Force_Quiet);
|
|
begin
|
|
if Code /= 0 then
|
|
raise Program_Error with "Exit code:" & Code'Image;
|
|
end if;
|
|
end Spawn;
|
|
|
|
------------------
|
|
-- Spawn_Bypass --
|
|
------------------
|
|
|
|
procedure Spawn_Bypass (Command : String;
|
|
Arguments : String := "")
|
|
is
|
|
Code : constant Integer := Spawn (Locate_In_Path (Command),
|
|
Argument_String_To_List (Arguments).all);
|
|
begin
|
|
if Code /= 0 then
|
|
raise Program_Error with "Exit code:" & Code'Image;
|
|
end if;
|
|
end Spawn_Bypass;
|
|
|
|
------------------
|
|
-- Enter_Folder --
|
|
------------------
|
|
|
|
function Enter_Folder (Path : String) return Folder_Guard is
|
|
Current : constant String := Ada.Directories.Current_Directory;
|
|
begin
|
|
return Guard : Folder_Guard (Current'Length) do
|
|
Guard.Original := Current;
|
|
Log ("Entering folder: " & Path, Debug);
|
|
Ada.Directories.Set_Directory (Path);
|
|
Guard.Initialized := True;
|
|
end return;
|
|
end Enter_Folder;
|
|
|
|
----------------------------
|
|
-- Stay_In_Current_Folder --
|
|
----------------------------
|
|
|
|
function Stay_In_Current_Folder return Folder_Guard is
|
|
begin
|
|
return Guard : Folder_Guard (0) do
|
|
Log ("Staying in current folder", Debug);
|
|
Guard.Initialized := False;
|
|
end return;
|
|
end Stay_In_Current_Folder;
|
|
|
|
--------------
|
|
-- Finalize --
|
|
--------------
|
|
|
|
overriding procedure Finalize (This : in out Folder_Guard) is
|
|
begin
|
|
if This.Initialized then
|
|
Log ("Going back to folder: " & This.Original, Debug);
|
|
Ada.Directories.Set_Directory (This.Original);
|
|
-- FIXME: what if this throws?
|
|
end if;
|
|
end Finalize;
|
|
|
|
end Alire.OS_Lib;
|