aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorRichard Carlsson <richardc@klarna.com>2011-06-14 15:34:11 +0200
committerHenrik Nord <henrik@erlang.org>2011-10-17 15:50:20 +0200
commit36a4d64ae7f39448ceb7c87bc4c8c0a9a9b72e1c (patch)
treedd625de7335dc8e4cf12c5013fa2b5f26c345967 /lib
parentaa79abf8ddff07295cab0431984a88a13592f35b (diff)
downloadotp-36a4d64ae7f39448ceb7c87bc4c8c0a9a9b72e1c.tar.gz
otp-36a4d64ae7f39448ceb7c87bc4c8c0a9a9b72e1c.tar.bz2
otp-36a4d64ae7f39448ceb7c87bc4c8c0a9a9b72e1c.zip
Make epp search directory of current file first when including another file
The expected behaviour of a C-style preprocessor (such as Erlang's epp) is to allow a header file to include another header file in the same directory even though that directory is not explicitly in the include path, and even if the actual include path might reference another directory containing a file with the same name. For example, if src/foo.erl explicitly includes "../include/foo.hrl", then foo.hrl should be able to include "bar.hrl" in that same directory even though "../include" might not be in the search path, and even if another file named bar.hrl could be found using the search path it should not override the one in the same directory as foo.hrl. In Erlang, the most common situation is that a user of an installed application includes a main public header file using include_lib ("appname/include/foo.hrl") and that file includes a secondary header file "bar.hrl". However, if it does this using include_lib, it causes a bootstrapping problem - in the build environment for the application itself, the application is not necessarily found by name. On the other hand, if foo.hrl uses a plain include, then bar.hrl might be found when the application is built (if explicit paths are set in the makefils) but not later on when a user includes the main header file of the installed application via include_lib. By making -include always look in the directory of the current file before it uses the search path, this problem is remedied, and include directives behave in a more intuitive way. This completes a partial fix in R11 that only worked for include_lib().
Diffstat (limited to 'lib')
-rw-r--r--lib/stdlib/src/epp.erl28
-rw-r--r--lib/stdlib/test/epp_SUITE.erl20
-rw-r--r--lib/stdlib/test/epp_SUITE_data/bar.hrl4
-rw-r--r--lib/stdlib/test/epp_SUITE_data/include/bar.hrl3
-rw-r--r--lib/stdlib/test/epp_SUITE_data/include/foo.hrl4
-rw-r--r--lib/stdlib/test/epp_SUITE_data/include_local.erl6
6 files changed, 51 insertions, 14 deletions
diff --git a/lib/stdlib/src/epp.erl b/lib/stdlib/src/epp.erl
index 230a4a0612..ccc14610d7 100644
--- a/lib/stdlib/src/epp.erl
+++ b/lib/stdlib/src/epp.erl
@@ -267,8 +267,10 @@ init_server(Pid, Name, File, AtLocation, Path, Pdm, Pre) ->
case user_predef(Pdm, Ms0) of
{ok,Ms1} ->
epp_reply(Pid, {ok,self()}),
+ %% ensure directory of current source file is first in path
+ Path1 = [filename:dirname(Name) | Path],
St = #epp{file=File, location=AtLocation, delta=0, name=Name,
- name2=Name, path=Path, macs=Ms1, pre_opened = Pre},
+ name2=Name, path=Path1, macs=Ms1, pre_opened = Pre},
From = wait_request(St),
enter_file_reply(From, Name, AtLocation, AtLocation),
wait_req_scan(St);
@@ -360,18 +362,18 @@ wait_req_skip(St, Sis) ->
From = wait_request(St),
skip_toks(From, St, Sis).
-%% enter_file(Path, FileName, IncludeToken, From, EppState)
+%% enter_file(FileName, IncludeToken, From, EppState)
%% leave_file(From, EppState)
%% Handle entering and leaving included files. Notify caller when the
%% current file is changed. Note it is an error to exit a file if we are
%% in a conditional. These functions never return.
-enter_file(_Path, _NewName, Inc, From, St)
+enter_file(_NewName, Inc, From, St)
when length(St#epp.sstk) >= 8 ->
epp_reply(From, {error,{abs_loc(Inc),epp,{depth,"include"}}}),
wait_req_scan(St);
-enter_file(Path, NewName, Inc, From, St) ->
- case file:path_open(Path, NewName, [read]) of
+enter_file(NewName, Inc, From, St) ->
+ case file:path_open(St#epp.path, NewName, [read]) of
{ok,NewF,Pname} ->
Loc = start_loc(St#epp.location),
wait_req_scan(enter_file2(NewF, Pname, From, St, Loc));
@@ -384,13 +386,16 @@ enter_file(Path, NewName, Inc, From, St) ->
%% Set epp to use this file and "enter" it.
enter_file2(NewF, Pname, From, St, AtLocation) ->
- enter_file2(NewF, Pname, From, St, AtLocation, []).
-
-enter_file2(NewF, Pname, From, St, AtLocation, ExtraPath) ->
Loc = start_loc(AtLocation),
enter_file_reply(From, Pname, Loc, AtLocation),
Ms = dict:store({atom,'FILE'}, {none,[{string,Loc,Pname}]}, St#epp.macs),
- Path = St#epp.path ++ ExtraPath,
+ %% update the head of the include path to be the directory of the new
+ %% source file, so that an included file can always include other files
+ %% relative to its current location (this is also how C does it); note
+ %% that the directory of the parent source file (the previous head of
+ %% the path) must be dropped, otherwise the path used within the current
+ %% file will depend on the order of file inclusions in the parent files
+ Path = [filename:dirname(Pname) | tl(St#epp.path)],
#epp{file=NewF,location=Loc,name=Pname,delta=0,
sstk=[St|St#epp.sstk],path=Path,macs=Ms}.
@@ -655,7 +660,7 @@ scan_undef(_Toks, Undef, From, St) ->
scan_include([{'(',_Llp},{string,_Lf,NewName0},{')',_Lrp},{dot,_Ld}], Inc,
From, St) ->
NewName = expand_var(NewName0),
- enter_file(St#epp.path, NewName, Inc, From, St);
+ enter_file(NewName, Inc, From, St);
scan_include(_Toks, Inc, From, St) ->
epp_reply(From, {error,{abs_loc(Inc),epp,{bad,include}}}),
wait_req_scan(St).
@@ -687,9 +692,8 @@ scan_include_lib([{'(',_Llp},{string,_Lf,NewName0},{')',_Lrp},{dot,_Ld}],
LibName = fname_join([LibDir | Rest]),
case file:open(LibName, [read]) of
{ok,NewF} ->
- ExtraPath = [filename:dirname(LibName)],
wait_req_scan(enter_file2(NewF, LibName, From,
- St, Loc, ExtraPath));
+ St, Loc));
{error,_E2} ->
epp_reply(From,
{error,{abs_loc(Inc),epp,
diff --git a/lib/stdlib/test/epp_SUITE.erl b/lib/stdlib/test/epp_SUITE.erl
index 57f3f4eddb..e58022f4da 100644
--- a/lib/stdlib/test/epp_SUITE.erl
+++ b/lib/stdlib/test/epp_SUITE.erl
@@ -20,7 +20,7 @@
-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
init_per_group/2,end_per_group/2]).
--export([rec_1/1, predef_mac/1,
+-export([rec_1/1, include_local/1, predef_mac/1,
upcase_mac_1/1, upcase_mac_2/1,
variable_1/1, otp_4870/1, otp_4871/1, otp_5362/1,
pmod/1, not_circular/1, skip_header/1, otp_6277/1, otp_7702/1,
@@ -63,7 +63,7 @@ end_per_testcase(_, Config) ->
suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
- [rec_1, {group, upcase_mac}, predef_mac,
+ [rec_1, {group, upcase_mac}, include_local, predef_mac,
{group, variable}, otp_4870, otp_4871, otp_5362, pmod,
not_circular, skip_header, otp_6277, otp_7702, otp_8130,
overload_mac, otp_8388, otp_8470, otp_8503, otp_8562,
@@ -97,6 +97,22 @@ rec_1(Config) when is_list(Config) ->
?line check_errors(List),
ok.
+include_local(doc) ->
+ [];
+include_local(suite) ->
+ [];
+include_local(Config) when is_list(Config) ->
+ ?line DataDir = ?config(data_dir, Config),
+ ?line File = filename:join(DataDir, "include_local.erl"),
+ %% include_local.erl includes include/foo.hrl which
+ %% includes bar.hrl (also in include/) without requiring
+ %% any additional include path, and overriding any file
+ %% of the same name that the path points to
+ ?line {ok, List} = epp:parse_file(File, [DataDir], []),
+ ?line {value, {attribute,_,a,{true,true}}} =
+ lists:keysearch(a,3,List),
+ ok.
+
%%% Here is a little reimplementation of epp:parse_file, which times out
%%% after 4 seconds if the epp server doesn't respond. If we use the
%%% regular epp:parse_file, the test case will time out, and then epp
diff --git a/lib/stdlib/test/epp_SUITE_data/bar.hrl b/lib/stdlib/test/epp_SUITE_data/bar.hrl
new file mode 100644
index 0000000000..01c527d549
--- /dev/null
+++ b/lib/stdlib/test/epp_SUITE_data/bar.hrl
@@ -0,0 +1,4 @@
+%% should not be included from include/foo.hrl even though the
+%% include path points here - include/bar.hrl overrides it
+
+-define(BAR_HRL, false).
diff --git a/lib/stdlib/test/epp_SUITE_data/include/bar.hrl b/lib/stdlib/test/epp_SUITE_data/include/bar.hrl
new file mode 100644
index 0000000000..038d3c900e
--- /dev/null
+++ b/lib/stdlib/test/epp_SUITE_data/include/bar.hrl
@@ -0,0 +1,3 @@
+%% included from foo.hrl in same directory
+
+-define(BAR_HRL, true).
diff --git a/lib/stdlib/test/epp_SUITE_data/include/foo.hrl b/lib/stdlib/test/epp_SUITE_data/include/foo.hrl
new file mode 100644
index 0000000000..a6dfa3d18a
--- /dev/null
+++ b/lib/stdlib/test/epp_SUITE_data/include/foo.hrl
@@ -0,0 +1,4 @@
+%% includes bar.hrl in same directory
+
+-define(FOO_HRL, true).
+-include("bar.hrl").
diff --git a/lib/stdlib/test/epp_SUITE_data/include_local.erl b/lib/stdlib/test/epp_SUITE_data/include_local.erl
new file mode 100644
index 0000000000..c8e155a064
--- /dev/null
+++ b/lib/stdlib/test/epp_SUITE_data/include_local.erl
@@ -0,0 +1,6 @@
+
+-module(include_local).
+
+-include("include/foo.hrl").
+
+-a({?FOO_HRL, ?BAR_HRL}).