Skip to content

Commit e7f3754

Browse files
Added Busybox preprocessing
1 parent 2ba2ac9 commit e7f3754

1 file changed

Lines changed: 382 additions & 0 deletions

File tree

Lines changed: 382 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,382 @@
1+
package de.variantsync.evolution.repository;
2+
3+
import de.variantsync.evolution.variability.SPLCommit;
4+
import org.eclipse.jgit.api.errors.GitAPIException;
5+
6+
import java.io.*;
7+
import java.nio.charset.StandardCharsets;
8+
import java.nio.file.Path;
9+
import java.util.ArrayList;
10+
import java.util.List;
11+
12+
import net.ssehub.kernel_haven.util.null_checks.NonNull;
13+
14+
import static net.ssehub.kernel_haven.util.null_checks.NullHelpers.notNull;
15+
16+
/***
17+
* A specialized SPLRepository that performs the necessary preprocessing of BusyBox source files, whenever a new
18+
* commit is checked out. The preprocessing includes splitting of lines into multiple lines in order to deal with
19+
* inline preprocessor macros used to model variability.
20+
* <br>
21+
* The preprocessing was copied from KernelHaven: net.ssehub.kernel_haven.busyboot.PrepareBusybox;
22+
* Copyright 2018-2019 University of Hildesheim, Software Systems Engineering
23+
* Licensed under the Apache License, Version 2.0 (the "License")
24+
*/
25+
public class BusyboxRepository extends SPLRepository {
26+
27+
public BusyboxRepository(Path path) {
28+
super(path);
29+
}
30+
31+
@Override
32+
public SPLCommit checkoutCommit(final SPLCommit c, boolean forced) throws GitAPIException, IOException {
33+
SPLCommit previousCommit = super.checkoutCommit(c, forced);
34+
BusyboxRepository.normalizeDir(this.getPath().toFile());
35+
return previousCommit;
36+
}
37+
38+
@Override
39+
public void checkoutBranch(final Branch branch) throws GitAPIException, IOException {
40+
super.checkoutBranch(branch);
41+
BusyboxRepository.normalizeDir(this.getPath().toFile());
42+
}
43+
44+
/*
45+
* The code below was copied from package net.ssehub.kernel_haven.busyboot;
46+
* Copyright 2018-2019 University of Hildesheim, Software Systems Engineering
47+
*
48+
* Licensed under the Apache License, Version 2.0 (the "License");
49+
* you may not use this file except in compliance with the License.
50+
* You may obtain a copy of the License at
51+
*
52+
* https://www.apache.org/licenses/LICENSE-2.0
53+
*
54+
* Unless required by applicable law or agreed to in writing, software
55+
* distributed under the License is distributed on an "AS IS" BASIS,
56+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
57+
* See the License for the specific language governing permissions and
58+
* limitations under the License.
59+
*
60+
* NOTE: The original version of this file was changed by Alexander Schultheiß,
61+
* Humboldt-Universität zu Berlin, [email protected]
62+
*/
63+
64+
/**
65+
* Starting point for modifying the c preprocessor source files based on Manuel Zerpies Busyfix.
66+
*
67+
* @param dir The directory to normalize all source files in.
68+
* @throws IOException If writing the replaced files fails.
69+
*/
70+
private static void normalizeDir(@NonNull final File dir) throws IOException {
71+
final File[] files = dir.listFiles();
72+
if (files != null) {
73+
for (final File file : files) {
74+
if (file.isDirectory()) {
75+
normalizeDir(file);
76+
} else if (file.getName().endsWith(".h") || file.getName().endsWith(".c")) {
77+
normalizeFile(file);
78+
}
79+
}
80+
}
81+
}
82+
83+
/**
84+
* Normalizes a single file in style of Busyfix.
85+
*
86+
* @param file The file to normalize.
87+
* @throws IOException If writing the replaced file fails.
88+
*/
89+
private static void normalizeFile(@NonNull final File file) throws IOException {
90+
final File tempFile;
91+
if (file.getName().contains("unicode") || file.getName().contains(".fnt")) {
92+
return;
93+
}
94+
95+
List<@NonNull String> inputFile = new ArrayList<>();
96+
final FileOutputStream fos;
97+
try (final BufferedReader br = new BufferedReader(new InputStreamReader(
98+
new FileInputStream(file.getPath()), StandardCharsets.UTF_8))) {
99+
String line;
100+
while ((line = br.readLine()) != null) {
101+
inputFile.add(line);
102+
}
103+
file.delete();
104+
tempFile = file;
105+
fos = new FileOutputStream(tempFile);
106+
}
107+
108+
inputFile = substituteLineContinuation(inputFile);
109+
110+
try (fos; final BufferedWriter bwr = new BufferedWriter(new OutputStreamWriter(fos))) {
111+
for (final String line : inputFile) {
112+
bwr.write(normalizeLine(line));
113+
bwr.write('\n');
114+
}
115+
}
116+
}
117+
118+
/**
119+
* Substitutes line continuation in Busybox for easier transformation.
120+
* <p>
121+
* Package visibility for test cases.
122+
*
123+
* @param inputFile The input file as a list of lines.
124+
* @return The list of lines with substituted line continuation
125+
*/
126+
static @NonNull List<@NonNull String> substituteLineContinuation(@NonNull final List<@NonNull String> inputFile) {
127+
int start = -1;
128+
int end = -1;
129+
final List<@NonNull String> toReturn = new ArrayList<>(inputFile.size());
130+
131+
for (int i = 0; i < inputFile.size(); i++) {
132+
if (notNull(inputFile.get(i)).endsWith("\\")) {
133+
if (start == -1) {
134+
start = i;
135+
}
136+
end = i;
137+
continue;
138+
} else {
139+
end = i;
140+
}
141+
if (start != -1) {
142+
final StringBuilder toAdd = new StringBuilder();
143+
for (int j = start; j <= end; j++) {
144+
String line = notNull(inputFile.get(j));
145+
if (j != end) {
146+
line = line.substring(0, line.length() - 1); // remove trailing \
147+
}
148+
toAdd.append(line);
149+
}
150+
toReturn.add(toAdd.toString());
151+
start = -1;
152+
end = -1;
153+
} else {
154+
toReturn.add(inputFile.get(i));
155+
end = -1;
156+
}
157+
}
158+
159+
// we found a \ at the last line of the file
160+
if (start != -1) {
161+
final StringBuilder toAdd = new StringBuilder();
162+
for (int j = start; j <= end; j++) {
163+
String line = notNull(inputFile.get(j));
164+
line = line.substring(0, line.length() - 1); // remove trailing \
165+
toAdd.append(line);
166+
}
167+
toReturn.add(toAdd.toString());
168+
}
169+
170+
return toReturn;
171+
}
172+
173+
/**
174+
* Normalizes a single line in style of Busyfix.
175+
*
176+
* @param line The line to normalize
177+
* @return The normalized line.
178+
*/
179+
private static @NonNull String normalizeLine(@NonNull final String line) {
180+
final int index;
181+
String temp;
182+
if (line.length() == 0) {
183+
return line;
184+
}
185+
186+
if (doNotNormalizeDefUndef(line)) {
187+
return line;
188+
}
189+
190+
// don't normalize comments
191+
if (line.contains("//")) {
192+
index = line.indexOf("//");
193+
return normalizeLine(notNull(line.substring(0, index))) + line.substring(index);
194+
}
195+
final boolean startsWith = line.replace("\\t", " ").trim().startsWith("*");
196+
if (line.contains("/*") || line.contains("*/") || startsWith) {
197+
// lines that start with or are block comments
198+
if (line.replace("\\t", " ").trim().startsWith("/*") || startsWith) {
199+
// fully comment
200+
if (!line.contains("*/")) {
201+
return line;
202+
203+
} else {
204+
return line.substring(0, line.indexOf("*/") + 2)
205+
+ normalizeLine(notNull(line.substring(line.indexOf("*/") + 2)));
206+
}
207+
208+
} else if (line.contains("/*")) {
209+
return normalizeLine(notNull(line.substring(0, line.indexOf("/*"))))
210+
+ line.substring(line.indexOf("/*"));
211+
212+
}
213+
}
214+
// malformed comments in scripts/basic/fixdep.c
215+
if (line.contains("if (!memcmp(p, \"IF_NOT\", 6)) goto conf7")
216+
|| line.contains("/*if (!memcmp(p, \"IF_\", 3)) ...*/")) {
217+
return line;
218+
}
219+
temp = normalizeDefinedEnableMacro(line);
220+
temp = normalizeEnableMacro(temp);
221+
temp = normalizeEnableInline(temp);
222+
temp = normalizeIf(temp);
223+
return temp;
224+
}
225+
226+
/**
227+
* Checks whether the given line is a #define or #undef line.
228+
*
229+
* @param line The line to check.
230+
* @return Whether the given line is a #define or #undef.
231+
*/
232+
private static boolean doNotNormalizeDefUndef(@NonNull final String line) {
233+
return line.contains("#undef") || line.contains("#define") || line.contains("# define")
234+
|| line.contains("# undef");
235+
}
236+
237+
/**
238+
* Normalize defined enable macro in Busyfix style.
239+
*
240+
* @param line The line to normalize.
241+
* @return The normalized line.
242+
*/
243+
private static @NonNull String normalizeDefinedEnableMacro(@NonNull final String line) {
244+
return notNull(line.replace("defined ENABLE_", "defined CONFIG_"));
245+
}
246+
247+
/**
248+
* Normalizes enable macro in Busyfix style.
249+
*
250+
* @param temp The string to normalize.
251+
* @return The normalized string.
252+
*/
253+
private static @NonNull String normalizeEnableMacro(@NonNull String temp) {
254+
if (temp.contains("if ENABLE_")) {
255+
temp = notNull(temp.replace("if ENABLE_", "if defined CONFIG_"));
256+
}
257+
if (temp.contains("if !ENABLE_")) {
258+
temp = notNull(temp.replace("if !ENABLE_", "if !defined CONFIG_"));
259+
}
260+
if (temp.contains("|| ENABLE_")) {
261+
temp = notNull(temp.replace("ENABLE_", "defined CONFIG_"));
262+
}
263+
if (temp.contains("&& ENABLE_")) {
264+
temp = notNull(temp.replace("ENABLE_", "'defined CONFIG_"));
265+
}
266+
if (temp.contains("|| !ENABLE_")) {
267+
temp = notNull(temp.replace("!ENABLE_", "!defined CONFIG_"));
268+
}
269+
if (temp.contains("&& !ENABLE_")) {
270+
temp = notNull(temp.replace("!ENABLE_", "'!defined CONFIG_"));
271+
}
272+
273+
return temp;
274+
}
275+
276+
/**
277+
* Normalizes enable inline in Busyfix style.
278+
*
279+
* @param line The line to normalize.
280+
* @return The normalized line.
281+
*/
282+
private static @NonNull String normalizeEnableInline(@NonNull String line) {
283+
284+
if (line.contains("_ENABLE_") || line.contains("#if")) {
285+
return line;
286+
}
287+
if (line.contains("ENABLE_")) {
288+
line = notNull(line.replace("ENABLE_", "\n#if defined CONFIG_"));
289+
final StringBuilder strB = new StringBuilder(line);
290+
final int indexDefinedConfig = line.indexOf(")", line.indexOf("defined CONFIG_") + 10);
291+
if (line.contains("if (\n#if defined CONFIG_")) {
292+
try {
293+
strB.insert(indexDefinedConfig, "\n1\n#else\n0\n#endif\n");
294+
} catch (final StringIndexOutOfBoundsException exc) {
295+
try {
296+
strB.insert(indexDefinedConfig, "\n1\n#else\n0\n#endif\n");
297+
} catch (final Exception exc2) {
298+
strB.append("\n1\n#else\n0\n#endif\n");
299+
}
300+
301+
}
302+
line = notNull(strB.toString());
303+
return line;
304+
}
305+
306+
// findOutWhat CONFIG_X is followed by and at which index of string
307+
final int indexOfWhitespace = line.indexOf(" ", line.indexOf("defined CONFIG_") + 10);
308+
final int indexOfComma = line.indexOf(",", line.indexOf("defined CONFIG_") + 10);
309+
int indexToInsert = indexOfWhitespace;
310+
if (indexOfComma != -1 && (indexOfComma < indexToInsert || indexToInsert == -1)) {
311+
indexToInsert = indexOfComma;
312+
}
313+
if (indexDefinedConfig != -1 && (indexDefinedConfig < indexToInsert || indexToInsert == -1)) {
314+
indexToInsert = indexOfComma;
315+
}
316+
if (indexToInsert != -1) {
317+
strB.insert(indexToInsert, "\n1\n#else\n0\n#endif\n");
318+
} else {
319+
strB.append("\n1\n#else\n0\n#endif\n");
320+
321+
}
322+
line = notNull(strB.toString());
323+
}
324+
return line;
325+
}
326+
327+
/**
328+
* Normalizes if structures in Busyfix style.
329+
*
330+
* @param line The line to do normalization in.
331+
* @return The normalized line.
332+
*/
333+
private static @NonNull String normalizeIf(@NonNull String line) {
334+
if (!line.contains("IF_")) {
335+
return line;
336+
}
337+
String variable = "";
338+
String init = "";
339+
String toRet;
340+
341+
if (line.contains("(") && line.contains(")")) {
342+
final int indexOpening = line.indexOf("(", line.indexOf("IF_"));
343+
int indexClosing = line.length() - 1;
344+
345+
int openingCount = 0;
346+
347+
final char[] chars = line.toCharArray();
348+
349+
for (int i = indexOpening + 1; i < chars.length; i++) {
350+
if (chars[i] == '(') {
351+
openingCount++;
352+
} else if (chars[i] == ')') {
353+
if (openingCount == 0) {
354+
indexClosing = i;
355+
break;
356+
}
357+
openingCount--;
358+
}
359+
}
360+
if (indexOpening >= 0 && indexClosing >= 0) {
361+
variable = line.substring(indexOpening, indexClosing);
362+
init = "\n" + line.substring(indexClosing + 1);
363+
364+
line = notNull(line.substring(0, indexOpening));
365+
}
366+
}
367+
if (line.contains("IF_NOT_")) {
368+
line = notNull(line.replace("IF_NOT_", "\n#if !defined CONFIG_"));
369+
} else if (line.contains("IF_")) {
370+
line = notNull(line.replace("IF_", "\n#if defined CONFIG_"));
371+
}
372+
373+
toRet = line + "\n";
374+
if (variable.length() != 0) {
375+
toRet += variable.substring(1);
376+
}
377+
toRet += "\n#endif" + init;
378+
return toRet;
379+
}
380+
381+
382+
}

0 commit comments

Comments
 (0)