|
1 #!perl |
|
2 |
|
3 # Release notes generator |
|
4 # 07/10/1999 - 18/11/99 RH |
|
5 |
|
6 use strict; |
|
7 |
|
8 sub PrintLines { print OUTFILE join("\n",@_),"\n"; } |
|
9 |
|
10 if (@ARGV!=2 || ! -e $ARGV[0]) |
|
11 { |
|
12 #........1.........2.........3.........4.........5.........6.........7..... |
|
13 print <<USAGE_EOF; |
|
14 |
|
15 -------------------------------------------------------------------------- |
|
16 Usage |
|
17 ----- |
|
18 perl relnotes.pl <listfile> <path> |
|
19 |
|
20 Generates an HTML document containing release notes for each |
|
21 component. |
|
22 |
|
23 <listfile> is a file containing a list of components and locations |
|
24 in the format: |
|
25 COMPNAME DIRECTORY [CH#1 CH#2] |
|
26 eg |
|
27 AGNMODEL AGNMODEL |
|
28 Release documentation is collexted for all relevant changelists |
|
29 in the range specified by <CH#1> and <CH#2>. |
|
30 |
|
31 The first line of the file must contain the default values for |
|
32 <CH#1> and <CH#2> separated by spaces. |
|
33 |
|
34 <path> is a path specifier (which may include wildcards)that, |
|
35 combined with the current view and the directory information in |
|
36 <listfile>, gives unambiguous access to component source on the |
|
37 appropriate branch(es) in the repository. |
|
38 |
|
39 The file name of the component list is assumed to be the same as the |
|
40 name of the target product. |
|
41 |
|
42 It is also assumed that revision specifier <CH#1> is earlier than |
|
43 revision specifier <CH#2>. |
|
44 |
|
45 Note |
|
46 ---- |
|
47 Before using this utility, you must switch to a client that gives |
|
48 visibility to those branches of the repository that contain the |
|
49 submissions for which you want to extract release note documentation. |
|
50 |
|
51 Example for Crystal |
|
52 ------------------- |
|
53 Switch to a client that gives visibility to only //EPOC/Main/generic |
|
54 and //EPOC/Main/Crystal. Then type: |
|
55 |
|
56 perl relnotes.pl crystal.dat //EPOC/Main |
|
57 |
|
58 This generates release notes in a file named Crystal.html |
|
59 -------------------------------------------------------------------------- |
|
60 |
|
61 USAGE_EOF |
|
62 exit 1; |
|
63 } |
|
64 |
|
65 my $listfile = $ARGV[0]; |
|
66 my $srcpath = $ARGV[1]; |
|
67 my $productname = $listfile; |
|
68 |
|
69 $productname =~ s#.*[\\\/]##; # lose any leading path |
|
70 $productname =~ s#\.[^\.]+$##; # lose any trailing extension |
|
71 |
|
72 $productname = ucfirst lc $productname; # capitalise only first letter |
|
73 $productname =~ s/_os_/_OS_/i; # Just making sure that OS is in caps |
|
74 $productname =~ s/_gt$/_GT/i; # Just making sure that GT is in caps |
|
75 $productname =~ s/_tv$/_TV/i; # Just making sure that TV is in caps |
|
76 |
|
77 $srcpath =~ s/\/\.\.\.$//; # remove trailing /..., which slows things down |
|
78 |
|
79 open INFILE, "< $listfile" or die "ERROR: Can't read $listfile\n"; |
|
80 |
|
81 |
|
82 my @listfile = <INFILE>; |
|
83 my $firstline = shift @listfile; |
|
84 |
|
85 $firstline =~ m/^\s*(\d+)\s+(\d+)/; |
|
86 my $default_firstchange = $1; |
|
87 my $default_lastchange = $2; |
|
88 |
|
89 if (!($default_firstchange > 0) || !($default_lastchange > 0)) |
|
90 { |
|
91 die "ERROR: First line of $listfile must contain non-zero changelist numbers only\n"; |
|
92 } |
|
93 |
|
94 my ( $s, $min, $hour, $mday, $mon, $year, $w, $y, $i)= localtime(time); |
|
95 $year+= 1900; |
|
96 $mon++; |
|
97 |
|
98 open OUTFILE, "> $productname.html" or die "ERROR: Can't open $productname.html for output"; |
|
99 print OUTFILE <<HEADING_EOF; |
|
100 <html>\n\n<head>\n<title>$productname Release Notes</title>\n</head>\n\n |
|
101 <body bgcolor=\"#ffffff\" text=\"#000000\" link=\"#5F9F9F\" vlink=\"5F9F9F\">\n |
|
102 <font face=verdana,arial,helvetica size=4>\n\n<hr>\n\n |
|
103 <a name=\"list\"><h1>$productname Release Notes</h1></a> |
|
104 <p>Created - $mday/$mon/$year\n |
|
105 HEADING_EOF |
|
106 |
|
107 my @newcomponents = (); |
|
108 my @nochangecomponents = (); |
|
109 |
|
110 foreach (@listfile) |
|
111 { |
|
112 my $firstchange = $default_firstchange; |
|
113 my $lastchange = $default_lastchange; |
|
114 |
|
115 my $newComponent = 0; |
|
116 |
|
117 s/\s*#.*$//; # remove comments |
|
118 if (/^\s*$/) { next; } # ignore blank lines |
|
119 |
|
120 if (/^\s*\S+\s+\S+\s+(\d+)\s+(\d+)/) # get any non-default changelist numbers |
|
121 { |
|
122 $firstchange = $1; |
|
123 $lastchange = $2; |
|
124 |
|
125 $newComponent = 1 if ($firstchange == 0); |
|
126 } |
|
127 |
|
128 if (/^\s*(\S+)\s+(\S+)/) # parse component data |
|
129 { |
|
130 my $compname = uc $1; |
|
131 my $topdir = $2; |
|
132 |
|
133 my $preform = 0; |
|
134 my $changeCount = 0; |
|
135 |
|
136 $firstchange++; # inc changelist number so we don't include the very first submission - it would have been picked up in the last run of this script |
|
137 |
|
138 print "Processing $compname\n"; |
|
139 my @complines = (); |
|
140 |
|
141 my $command = "p4 changes -l -s submitted $srcpath/$topdir/...\@$firstchange,$lastchange"; |
|
142 my @compchange = `$command`; |
|
143 die "ERROR: Could not execute: $command\n" if $?; |
|
144 |
|
145 foreach my $line (@compchange) |
|
146 { |
|
147 if ($line !~ /\S/) { next; } # ignore lines with no text |
|
148 chomp $line; |
|
149 $line =~ s/\&/&/g; |
|
150 $line =~ s/\</</g; |
|
151 $line =~ s/\>/>/g; |
|
152 $line =~ s/\"/"/g; |
|
153 |
|
154 if ($line =~ /^Change\s+\d+/i) |
|
155 { |
|
156 $changeCount+=1; |
|
157 $line =~ s/\s+by .*$//; |
|
158 if ($preform) |
|
159 { |
|
160 push @complines, "</pre>"; |
|
161 $preform = 0; |
|
162 } |
|
163 push @complines, "<p><b>$line</b>"; |
|
164 push @complines, "<pre>"; |
|
165 $preform = 1; |
|
166 next; |
|
167 } |
|
168 |
|
169 $line =~ s/^\s//; # drop first leading whitespace |
|
170 $line =~ s/^\t/ /; # shorten any leading tab |
|
171 if ($changeCount == 0) |
|
172 { |
|
173 warn "WARNING: Description contains text preceding \"Change\". Printing line to output file:\n$line\n"; |
|
174 } |
|
175 push @complines, $line; |
|
176 } |
|
177 if ($changeCount == 0) |
|
178 { |
|
179 if ($newComponent) |
|
180 { |
|
181 push @newcomponents, $compname; |
|
182 } |
|
183 else |
|
184 { |
|
185 push @nochangecomponents, $compname; |
|
186 } |
|
187 next; |
|
188 } |
|
189 # Component with real change descriptions |
|
190 if ($preform) |
|
191 { |
|
192 push @complines, "</pre>"; |
|
193 } |
|
194 &PrintLines("<h2>$compname</h2>",@complines); |
|
195 } |
|
196 } |
|
197 close INFILE; |
|
198 |
|
199 if (scalar @newcomponents) |
|
200 { |
|
201 &PrintLines("<h2>New Components</h2>", join(", ", sort @newcomponents)); |
|
202 } |
|
203 |
|
204 if (scalar @nochangecomponents) |
|
205 { |
|
206 &PrintLines("<h2>Unchanged Components</h2>", join(", ", sort @nochangecomponents)); |
|
207 } |
|
208 |
|
209 &PrintLines("</BODY></HTML>"); |
|
210 close OUTFILE; |