Merge lp:~epics-core/epics-base/config-from-snippets into lp:~epics-core/epics-base/3.16

Proposed by Ralph Lange
Status: Superseded
Proposed branch: lp:~epics-core/epics-base/config-from-snippets
Merge into: lp:~epics-core/epics-base/3.16
Diff against target: 307 lines (+265/-0)
5 files modified
configure/RULES_BUILD (+16/-0)
src/tools/Makefile (+1/-0)
src/tools/assembleSnippets.pl (+145/-0)
src/tools/test/Makefile (+1/-0)
src/tools/test/Snippets.plt (+102/-0)
To merge this branch: bzr merge lp:~epics-core/epics-base/config-from-snippets
Reviewer Review Type Date Requested Status
Andrew Johnson Pending
Review via email: mp+273997@code.launchpad.net

This proposal has been superseded by a proposal from 2015-10-09.

Description of the change

Adds the ability to create config files from snippets.

Documentation is still TBD.

Usage (e.g. in exampleApp/src/Makefile)

  DB_INSTALLS += $(COMMON_DIR)/ka.mm
  SCRIPTS += ka.nn

  COMMON_ASSEMBLE_FROM_SNIPPETS += ka.mm
  ka.mm_SNIPPETS = $(wildcard ../ka.d/*)
  ASSEMBLE_FROM_SNIPPETS += ka.nn
  ka.nn_SNIPPETS = $(wildcard ../ka.d/*)

This creates two (in this case identical) files:
ka.mm is created in the COMMON_DIR and installed in TOP/db.
ka.nn is created in the T_A dir and installed in TOP/bin/<arch>.

To post a comment you must log in.
Revision history for this message
Andrew Johnson (anj) wrote :

Is this intended for 3.16 (lp:epics-base) or 3.15 (lp:epics-base/3.15)? The merge proposal implies the former.

Haven't played with it yet and (of course) I'm going to try to think up something shorter than ASSEMBLE_FROM_SNIPPETS in the Makefile names, but overall the idea looks good.

Do you need a review quickly? I will be on vacation from 2015-10-14 through 2015-10-23 but I should be able to squeeze it in before I go.

12707. By Ralph Lange

Back out 12706 (assembly integration in build system)

12708. By Ralph Lange

tools: fix in assembleSnippets script and test for Windows (getpwuid() not implemented)

12709. By Ralph Lange

configure: add assemblies to RULES_EXPAND

12710. By Ralph Lange

tools: add new builtin macros to assemblies

12711. By Andrew Johnson

Minor RULES_EXPAND changes

Added support for a _PATTERN variable which calls $(wildcard) and
prepends .. and the other SRC_DIRS automatically.
Use $(ECHO) instead of @echo so make -s works silently.
Added another meta-rule to create .d files when make needs one.

Unmerged revisions

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'configure/RULES_BUILD'
2--- configure/RULES_BUILD 2015-07-24 20:59:38 +0000
3+++ configure/RULES_BUILD 2015-10-09 15:33:25 +0000
4@@ -316,6 +316,22 @@
5 $(LINK.mod)
6
7 #---------------------------------------------------------------
8+# Assembled files (from snippets)
9+
10+COMMON_ASSEMBLE_FROM_SNIPPETS_ += $(addprefix $(COMMON_DIR)/,$(COMMON_ASSEMBLE_FROM_SNIPPETS))
11+
12+$(COMMON_ASSEMBLE_FROM_SNIPPETS_): $(COMMON_DIR)/%:
13+ @echo "Assembling common file $@ from snippets"
14+ @$(RM) $(notdir $@)
15+ $(PERL) $(TOOLS)/assembleSnippets.pl -o $(notdir $@) $($(notdir $@)_SNIPPETS)
16+ @$(MV) $(notdir $@) $@
17+
18+$(ASSEMBLE_FROM_SNIPPETS): %:
19+ @echo "Assembling file $@ from snippets"
20+ @$(RM) $(notdir $@)
21+ $(PERL) $(TOOLS)/assembleSnippets.pl -o $(notdir $@) $($(notdir $@)_SNIPPETS)
22+
23+#---------------------------------------------------------------
24 # Automated testing
25
26 runtests: $(TESTSCRIPTS)
27
28=== modified file 'src/tools/Makefile'
29--- src/tools/Makefile 2015-07-10 19:02:01 +0000
30+++ src/tools/Makefile 2015-10-09 15:33:25 +0000
31@@ -33,6 +33,7 @@
32 PERL_MODULES += DBD/Registrar.pm
33 PERL_MODULES += DBD/Variable.pm
34
35+PERL_SCRIPTS += assembleSnippets.pl
36 PERL_SCRIPTS += convertRelease.pl
37 PERL_SCRIPTS += cvsclean.pl
38 PERL_SCRIPTS += dos2unix.pl
39
40=== added file 'src/tools/assembleSnippets.pl'
41--- src/tools/assembleSnippets.pl 1970-01-01 00:00:00 +0000
42+++ src/tools/assembleSnippets.pl 2015-10-09 15:33:25 +0000
43@@ -0,0 +1,145 @@
44+#!/usr/bin/env perl
45+#*************************************************************************
46+# Copyright (c) 2015 ITER Organization.
47+# EPICS BASE is distributed subject to a Software License Agreement found
48+# in file LICENSE that is included with this distribution.
49+#*************************************************************************
50+
51+# $Id$
52+
53+use strict;
54+use warnings;
55+
56+use Getopt::Std;
57+use File::Basename;
58+use Data::Dumper;
59+
60+our ($opt_o, $opt_d, $opt_m, $opt_i, $opt_M);
61+
62+$Getopt::Std::OUTPUT_HELP_VERSION = 1;
63+&HELP_MESSAGE if !getopts('M:i:m:o:d') || @ARGV == 0;
64+
65+my $out;
66+my $dep;
67+my %snippets;
68+my $ipattern;
69+
70+my $datetime = localtime();
71+(my $user, my @dummy) = getpwuid($<);
72+my %replacements = (
73+ _DATETIME_ => $datetime,
74+ _USER_ => $user,
75+);
76+
77+if ($opt_o) {
78+ open $out, '>', $opt_o or
79+ die "Can't create $opt_o: $!\n";
80+ print STDERR "opened file $opt_o for output\n" if $opt_d;
81+} else {
82+ open $out, '>&', STDOUT;
83+ print STDERR "using STDOUT for output\n" if $opt_d;
84+}
85+
86+if ($opt_m) {
87+ foreach my $r (split /,/, $opt_m) {
88+ (my $k, my $v) = split /=/, $r;
89+ $replacements{$k} = $v;
90+ }
91+}
92+
93+if ($opt_M) {
94+ open $dep, '>', $opt_M or
95+ die "Can't create $opt_M: $!\n";
96+ print STDERR "opened dependency file $opt_M for output\n" if $opt_d;
97+ print $dep basename($opt_o), ":";
98+}
99+
100+if ($opt_i) {
101+ $ipattern = qr($opt_i);
102+}
103+
104+# %snippets is a hash {rank}
105+# of hashes {name-after-rank}
106+# of arrays[] [files...]
107+# of arrays[2] [filename, command]
108+print STDERR "reading input files\n" if $opt_d;
109+foreach (@ARGV) {
110+ my $name = basename($_);
111+ if ($opt_i and not $name =~ /$ipattern/) {
112+ print STDERR " snippet $_ does not match input pattern $opt_i - ignoring\n" if $opt_d;
113+ next;
114+ }
115+ if ($name =~ /\A([ARD]?)([0-9]+)(.*[^~])\z/) {
116+ print STDERR " considering snippet $_\n" if $opt_d;
117+ if (exists $snippets{$2}) {
118+ my %rank = %{$snippets{$2}};
119+ my @files = @{ $rank{(keys %rank)[0]} };
120+ my $existcmd = $files[0]->[1];
121+ if ($1 eq "D" and $existcmd ne "D") {
122+ print STDERR " ignoring 'D' default for existing rank $2\n" if $opt_d;
123+ next;
124+ } elsif ($1 eq "R") {
125+ print STDERR " 'R' command - deleting existing rank $2 snippets\n" if $opt_d;
126+ $snippets{$2} = {};
127+ } elsif ($existcmd eq "D") {
128+ print STDERR " deleting existing rank $2 default snippet\n" if $opt_d;
129+ $snippets{$2} = {};
130+ }
131+ }
132+ if ($opt_d) {
133+ print STDERR " adding snippet ";
134+ print STDERR "marked as default " if $1 eq "D";
135+ print STDERR "to rank $2\n";
136+ }
137+ $snippets{$2}{$3} = () if (not exists $snippets{$2}{$3});
138+ push @{$snippets{$2}{$3}}, [ $_, $1 ];
139+ }
140+}
141+
142+if ($opt_d) {
143+ print STDERR "finished reading input files\n";
144+ print STDERR "dumping the final snippet structure\n";
145+ print STDERR Dumper(\%snippets);
146+ print STDERR "dumping the macro replacements\n";
147+ print STDERR Dumper(\%replacements);
148+ print STDERR "creating output\n";
149+}
150+
151+foreach my $r (sort {$a<=>$b} keys %snippets) {
152+ print STDERR " working on rank $r\n" if $opt_d;
153+ foreach my $n (sort keys %{$snippets{$r}}) {
154+ foreach my $s (@{$snippets{$r}{$n}}) {
155+ my $in;
156+ my $f = $s->[0];
157+ print STDERR " snippet $n from file $f\n" if $opt_d;
158+ open $in, '<', $f or die "Can't open $f: $!\n";
159+ print $dep " \\\n $f" if $opt_M;
160+ while (<$in>) {
161+ chomp;
162+ foreach my $k (keys %replacements) {
163+ s/$k/$replacements{$k}/g;
164+ }
165+ print $out $_, "\n";
166+ }
167+ close $in;
168+ }
169+ }
170+}
171+
172+print STDERR "finished creating output, closing\n" if $opt_d;
173+if ($opt_M) {
174+ print $dep "\n";
175+ close $dep;
176+}
177+close $out;
178+
179+sub HELP_MESSAGE {
180+ print STDERR "Usage: assembleSnippets.pl [options] snippets ...\n";
181+ print STDERR "Options:\n";
182+ print STDERR " -o file output file [STDOUT]\n";
183+ print STDERR " -d debug mode [no]\n";
184+ print STDERR " -m macros list of macro replacements as \"key=val,key=val\"\n";
185+ print STDERR " -i pattern pattern for input files to match\n";
186+ print STDERR " -M file write file with dependency rule suitable for make\n";
187+ exit 2;
188+}
189
190=== modified file 'src/tools/test/Makefile'
191--- src/tools/test/Makefile 2012-03-14 20:27:40 +0000
192+++ src/tools/test/Makefile 2015-10-09 15:33:25 +0000
193@@ -18,6 +18,7 @@
194 TESTS += Recfield
195 TESTS += Recordtype
196 TESTS += Registrar
197+TESTS += Snippets
198 TESTS += Variable
199
200 TESTSCRIPTS_HOST += $(TESTS:%=%.t)
201
202=== added file 'src/tools/test/Snippets.plt'
203--- src/tools/test/Snippets.plt 1970-01-01 00:00:00 +0000
204+++ src/tools/test/Snippets.plt 2015-10-09 15:33:25 +0000
205@@ -0,0 +1,102 @@
206+#!/usr/bin/env perl
207+
208+use File::Path;
209+
210+use Test::More tests => 26;
211+
212+(my $user, my @rest) = getpwuid($<);
213+
214+mkdir "a$$";
215+mkdir "b$$";
216+
217+sub mksnip {
218+ my ($dir, $file, $line) = @_;
219+ open(my $fh, '>', "$dir$$/$file") or die "can't open $dir$$/$file : $!";
220+ print $fh $line;
221+ close $fh;
222+}
223+
224+sub assemble {
225+ my @cmd = ( 'perl', '../../assembleSnippets.pl', '-o', "out$$" );
226+ push @cmd, @_;
227+ system(@cmd);
228+ open(my $fh, '<', "out$$") or die "can't open out$$ : $!";
229+ chomp(my @result = <$fh>);
230+ close $fh;
231+ return join (' ', @result);
232+}
233+
234+# Adding two snippets of same rank, sorting alphabetically
235+mksnip('a', '10_a', '10');
236+mksnip('b', '10_c', '12');
237+is assemble("a$$/10_a", "b$$/10_c"), '10 12', "adding same rank; ordered";
238+is assemble("b$$/10_c", "a$$/10_a"), '10 12', "adding same rank; reverse order";
239+
240+# Same, with 'A' cmd
241+mksnip('a', 'A10_a', '10');
242+mksnip('b', 'A10_c', '12');
243+is assemble("a$$/10_a", "b$$/A10_c"), '10 12', "'A' add same rank; ordered";
244+is assemble("b$$/10_c", "a$$/A10_a"), '10 12', "'A' add same rank; reverse order";
245+
246+# Same name does not create issues
247+mksnip('b', '10_a', '10x');
248+is assemble("a$$/10_a", "b$$/10_a"), '10 10x', "adding same name twice; order a-b";
249+is assemble("b$$/10_a", "a$$/10_a"), '10x 10', "adding same name twice; order b-a";
250+
251+# Backup files (trailing ~) and hidden files (leading '.') get ignored
252+mksnip('b', '10_c~', '12~');
253+mksnip('b', '.10_c', '.12');
254+is assemble("b$$/10_c", "b$$/10_c~"), '12', "backup file (trailing ~) gets ignored";
255+is assemble("b$$/10_c", "b$$/.10_c"), '12', "hidden file (leading .) gets ignored";
256+
257+# Non-numeric filenames get ignored
258+mksnip('a', 'foo10_a', 'foo10');
259+is assemble("b$$/10_c", "a$$/foo10_a"), '12', "file starting with [^ADR0-9] gets ignored";
260+
261+# 'R' command replaces existing snippets of same rank
262+mksnip('a', 'R10_b', '11');
263+is assemble("a$$/10_a", "b$$/10_c", "a$$/R10_b"), '11', "'R' cmd; replace all";
264+is assemble("a$$/10_a", "a$$/R10_b", "b$$/10_c"), '11 12', "'R' cmd; replace one (ordered)";
265+is assemble("b$$/10_c", "a$$/R10_b", "a$$/10_a"), '10 11', "'R' cmd; replace one (reverse order)";
266+
267+# 'D' command establishes default that gets overwritten or ignored
268+mksnip('a', 'D10_a', 'D10');
269+mksnip('b', 'D10_c', 'D12');
270+is assemble("a$$/D10_a", "b$$/10_c"), '12', "'D' default; replaced by regular";
271+is assemble("a$$/D10_a", "b$$/D10_c"), 'D12', "'D' default; replaced by new default (ordered)";
272+is assemble("b$$/D10_c", "a$$/D10_a"), 'D10', "'D' default; replaced by new default (reverse order)";
273+is assemble("a$$/D10_a", "a$$/R10_b"), '11', "'D' default; replaced by 'R' cmd";
274+is assemble("b$$/10_c", "a$$/D10_a"), '12', "'D' default; ignored when regular exists";
275+
276+# Ranks are sorted numerically
277+mksnip('b', '09_a', '09');
278+mksnip('a', '15_a', '15');
279+mksnip('b', '2_a', '2');
280+is assemble("a$$/10_a", "b$$/2_a", "a$$/15_a", "b$$/09_a"), '2 09 10 15', "ranks are sorted numerically";
281+
282+# Builtin macros
283+mksnip('a', '30_a', '_USER_');
284+is assemble("a$$/30_a"), "$user", "builtin macro _USER_";
285+
286+# User macros
287+mksnip('b', '35_a', 'Line _M1_');
288+mksnip('b', '35_b', 'Line _M1_ with _M2_');
289+mksnip('b', '35_c', 'Line _M2_ with _M2_');
290+is assemble("-m", "_M1_=REP1", "b$$/35_a"), "Line REP1", "single user macro; single occurrence";
291+is assemble("-m", "_M1_=REP1,_M2_=REP2", "b$$/35_b"), "Line REP1 with REP2", "multiple user macros";
292+is assemble("-m", "_M2_=REP2", "b$$/35_c"), "Line REP2 with REP2", "single user macro; multiple occurrences";
293+
294+# Input pattern
295+mksnip('a', '10_a.cmd', '10cmd');
296+is assemble("-i", "\.cmd", "a$$/10_a", "b$$/10_c", "a$$/R10_b", "a$$/10_a.cmd"), '10cmd', "input pattern";
297+
298+# Dependency file generation
299+assemble("-M", "./dep$$", "a$$/10_a", "b$$/10_c");
300+open(my $fh, '<', "dep$$") or die "can't open dep$$ : $!";
301+chomp(my @result = <$fh>);
302+close $fh;
303+is "$result[0]", "out$$: \\", "dependency file (line 1)";
304+is "$result[1]", " a$$/10_a \\", "dependency file (line 2)";
305+is "$result[2]", " b$$/10_c", "dependency file (line 3)";
306+
307+rmtree([ "a$$", "b$$", "out$$", "dep$$" ]);

Subscribers

People subscribed via source and target branches