Skip to content

Commit bcd5e87

Browse files
committed
Merge remote-tracking branch 'origin/develop' into develop
# Conflicts: # src/main/java/de/variantsync/evolution/Main.java
2 parents 394194e + 897d0b9 commit bcd5e87

2 files changed

Lines changed: 436 additions & 0 deletions

File tree

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

0 commit comments

Comments
 (0)