Skip to content

Commit ed99b09

Browse files
committed
Merge branch 'mr/improve_win32_env_import' into 'master'
Ensure GNATCOLL.OS.Process_Types.Import does not crash on Win32 See merge request eng/toolchain/gnatcoll-core!161
2 parents bf7b6a1 + 43588c5 commit ed99b09

File tree

10 files changed

+107
-17
lines changed

10 files changed

+107
-17
lines changed

core/src/os/gnatcoll-os-process_types__unix.adb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ package body GNATCOLL.OS.Process_Types is
7676
Set_Variable (Env, Name, Value);
7777
end Import_Var;
7878
begin
79+
Deallocate (Env);
7980
Env_Vars.Iterate (Import_Var'Unrestricted_Access);
8081
end Import;
8182

core/src/os/gnatcoll-os-process_types__win32.adb

Lines changed: 68 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@ with Ada.Strings.UTF_Encoding.Wide_Strings;
2525
with Ada.Wide_Characters.Handling;
2626
with Ada.Environment_Variables;
2727
with GNATCOLL.String_Builders;
28+
with GNATCOLL.OS.Win32.Process;
29+
pragma Warnings(Off);
30+
with System.Address_To_Access_Conversions;
31+
pragma Warning(On);
2832

2933
package body GNATCOLL.OS.Process_Types is
3034

@@ -33,10 +37,27 @@ package body GNATCOLL.OS.Process_Types is
3337
package Env_Vars renames Ada.Environment_Variables;
3438
package WCH renames Ada.Wide_Characters.Handling;
3539

40+
type EnvironW is array (1 .. Integer'Last) of Wide_Character;
41+
pragma Suppress_Initialization(EnvironW);
42+
type EnvironW_Access is access all EnvironW;
43+
for EnvironW_Access'Storage_Size use 0;
44+
-- This type is used to map the address returned by GetEnvironmentStrings
45+
-- to a Wide_Character array. Initialization is suppressed as only a part
46+
-- of the declared object will be used.
47+
-- The 0 storage size ensure we cannot call "new"
48+
49+
package EnvironW_Ops is new System.Address_To_Access_Conversions(EnvironW);
50+
use EnvironW_Ops;
51+
-- Provides To_Pointer to convert an Address to an EnvironW_Access.
52+
3653
Minimal_Env : Environ;
3754

3855
procedure Add_Minimal_Env (Env : in out Environ);
3956

57+
---------------------
58+
-- Add_Minimal_Env --
59+
---------------------
60+
4061
procedure Add_Minimal_Env (Env : in out Environ) is
4162
begin
4263
if Env_Vars.Exists ("SYSTEMROOT") then
@@ -91,14 +112,55 @@ package body GNATCOLL.OS.Process_Types is
91112
------------
92113

93114
procedure Import (Env : in out Environ) is
94-
procedure Import_Var (Name, Value : String);
115+
use GNATCOLL.OS.Win32.Process;
116+
use GNATCOLL.OS.Win32;
95117

96-
procedure Import_Var (Name, Value : String) is
97-
begin
98-
Set_Variable (Env, Name, Value);
99-
end Import_Var;
118+
-- Fetch the current process environment
119+
Env_Addr : System.Address := GetEnvironmentStrings;
120+
Env_Ptr : EnvironW_Access := EnvironW_Access(To_Pointer (Env_Addr));
121+
122+
Free_Result : BOOL;
123+
124+
-- Start of a variable definition
125+
Start : Integer := 0;
126+
127+
-- Current position in the environ block
128+
Idx : Integer := 1;
129+
130+
WNUL : constant Wide_Character := Wide_Character'Val (0);
131+
use all type System.Address;
100132
begin
101-
Env_Vars.Iterate (Import_Var'Unrestricted_Access);
133+
-- Start by resetting the environment
134+
Env.Inherited := False;
135+
WSLB.Deallocate (Env.Env);
136+
137+
-- Note that the environment may contain invalid UTF-16 strings. We
138+
-- should just ignore that and pass the value to our structure.
139+
loop
140+
if Env_Ptr (Idx) /= WNUL then
141+
if Start = 0 then
142+
Start := Idx;
143+
end if;
144+
else
145+
-- A NUL character marks the end of a variable definition
146+
if Start /= 0 then
147+
WSLB.Append (Env.Env, Wide_String (Env_Ptr (Start .. Idx - 1)));
148+
Start := 0;
149+
end if;
150+
151+
-- If a NUL character follows the end of a variable definition,
152+
-- the end of the environment block has been reached.
153+
exit when Env_Ptr (Idx + 1) = WNUL;
154+
end if;
155+
156+
Idx := Idx + 1;
157+
end loop;
158+
159+
Free_Result :=
160+
GNATCOLL.OS.Win32.Process.FreeEnvironmentStrings (Env_Addr);
161+
if Free_Result = BOOL_FALSE then
162+
raise OS_Error with "error while deallocating environment block";
163+
end if;
102164
end Import;
103165

104166
-------------

core/src/os/win32/gnatcoll-os-win32-process.ads

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,4 +174,14 @@ package GNATCOLL.OS.Win32.Process is
174174
Convention => Stdcall,
175175
External_Name => "NtQueryInformationProcess";
176176

177+
function GetEnvironmentStrings return System.Address
178+
with Import => True,
179+
Convention => Stdcall,
180+
External_Name => "GetEnvironmentStringsW";
181+
182+
function FreeEnvironmentStrings (Env : System.Address) return Bool
183+
with Import => True,
184+
Convention => Stdcall,
185+
External_Name => "FreeEnvironmentStringsW";
186+
177187
end GNATCOLL.OS.Win32.Process;

gprproject/gprbuild.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ def get_compiler_info(target: str | None) -> dict[str, str]:
2727

2828
process = run(gprconfig_cmd, capture_output=True)
2929
if process.returncode != 0:
30-
raise GPRError(f"error while trying to capture output of '{cmd}'")
30+
raise GPRError(f"error while trying to capture output of '{gprconfig_cmd}'")
3131
try:
3232
gprconfig_output = process.stdout.decode("utf-8").strip()
3333
except Exception:
@@ -97,7 +97,7 @@ def __init__(
9797
if prefix:
9898
self.prefix = os.path.abspath(prefix)
9999
else:
100-
self.prefix = re.findall(r" 1 path:(.*)", gprconfig_output)[0]
100+
self.prefix = re.findall(r" 1 path:(.*)", gprconfig_output)[0].strip()
101101
if self.prefix.endswith(os.sep):
102102
self.prefix = os.path.dirname(os.path.dirname(self.prefix))
103103
else:

gprproject/testsuite/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ def set_up(self) -> None:
8080
self.env.default_source_dirs = self.default_source_dirs
8181

8282
# Get some compiler info
83-
compiler_info = get_compiler_info(target=self.env.target.platform)
83+
compiler_info = get_compiler_info(target=self.env.target.triplet)
8484
self.env.llvm = "LLVM" in compiler_info.get("name", "GNAT")
8585
self.env.gcc = compiler_info.get("name", "GNAT") == "GNAT"
8686

gprproject/testsuite/drivers/__init__.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,12 @@ def bin_check_call(
160160
if test_name is None:
161161
test_name = driver.test_name
162162

163+
# Compute final environment
164+
final_env = None
165+
if env is not None:
166+
final_env = os.environ.copy()
167+
final_env.update(env)
168+
163169
if driver.env.is_cross:
164170
if driver.env.target.os.name == "windows":
165171
os.environ["WINEDEBUG"] = "-all"
@@ -172,7 +178,7 @@ def bin_check_call(
172178
subp = subprocess.Popen(
173179
cmd,
174180
cwd=cwd,
175-
env=env,
181+
env=final_env,
176182
stdin=subprocess.DEVNULL,
177183
stdout=subprocess.PIPE,
178184
stderr=subprocess.STDOUT,
@@ -218,7 +224,7 @@ def bin_check_call(
218224
subp = subprocess.Popen(
219225
cmd,
220226
cwd=cwd,
221-
env=env,
227+
env=final_env,
222228
stdin=subprocess.DEVNULL,
223229
stdout=subprocess.PIPE,
224230
stderr=subprocess.STDOUT,
@@ -233,7 +239,7 @@ def bin_check_call(
233239
"status": process.status,
234240
"cmd": cmd,
235241
"timeout": timeout,
236-
"env": env,
242+
"env": final_env,
237243
"cwd": cwd,
238244
}
239245
)

gprproject/testsuite/drivers/basic.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ def run(self):
100100
slot=self.slot,
101101
copy_files_on_target=copy_files_on_target,
102102
timeout=self.default_process_timeout,
103+
env=self.test_env.get('test_env')
103104
)
104105
self.output += process.out.decode("utf-8")
105106

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
11
title: GNATCOLL.OS.Process test inherit_env parameter
22
data: ["test_data/*.py"]
3+
# This is mainly done to ensure the test does not crash when environment contains
4+
# some non ascii value. Note that currently we don't know how to read correctly
5+
# that value on System such as Windows.
6+
test_env: {"UNICODE_ENV": "º"}
37
control:
4-
- [SKIP, "env.is_cross", "Test uses python which is not available on cross targets"]
8+
- [SKIP, "env.is_cross", "Test uses python which is not available on cross targets"]

testsuite/drivers/__init__.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,11 @@ def bin_check_call(
221221
if test_name is None:
222222
test_name = driver.test_name
223223

224+
final_env = None
225+
if env is not None:
226+
final_env = os.environ.copy()
227+
final_env.update(env)
228+
224229
if driver.env.is_cross:
225230
if driver.env.target.os.name == "windows":
226231
os.environ["WINEDEBUG"] = "-all"
@@ -233,7 +238,7 @@ def bin_check_call(
233238
subp = subprocess.Popen(
234239
cmd,
235240
cwd=cwd,
236-
env=env,
241+
env=final_env,
237242
stdin=subprocess.DEVNULL,
238243
stdout=subprocess.PIPE,
239244
stderr=subprocess.STDOUT,
@@ -279,7 +284,7 @@ def bin_check_call(
279284
subp = subprocess.Popen(
280285
cmd,
281286
cwd=cwd,
282-
env=env,
287+
env=final_env,
283288
stdin=subprocess.DEVNULL,
284289
stdout=subprocess.PIPE,
285290
stderr=subprocess.STDOUT,
@@ -294,7 +299,7 @@ def bin_check_call(
294299
"status": process.status,
295300
"cmd": cmd,
296301
"timeout": timeout,
297-
"env": env,
302+
"env": final_env,
298303
"cwd": cwd,
299304
}
300305
)

testsuite/drivers/basic.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,8 @@ def run(self):
7272
[os.path.join(self.test_env['working_dir'], test_exe)],
7373
slot=self.slot,
7474
copy_files_on_target=copy_files_on_target,
75-
timeout=self.default_process_timeout)
75+
timeout=self.default_process_timeout,
76+
env=self.test_env.get('test_env'))
7677
self.output += process.out.decode('utf-8')
7778

7879
# Store result output, so the python post test can access it if needed.

0 commit comments

Comments
 (0)