main.cpp
1 /*
2  name: tools/arcs1to2/main.cpp
3 
4  This file is part of ARCS - Augmented Reality Component System
5  (version 2-current), written by Jean-Yves Didier
6  for IBISC Laboratory (http://www.ibisc.univ-evry.fr)
7 
8  Copyright (C) 2013 Université d'Evry-Val d'Essonne
9 
10  This program is free software: you can redistribute it and/or modify
11  it under the terms of the GNU General Public License as published by
12  the Free Software Foundation, either version 2 of the License, or
13  (at your option) any later version.
14 
15  This program is distributed in the hope that it will be useful,
16  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18  GNU General Public License for more details.
19 
20  You should have received a copy of the GNU General Public License
21  along with this program. If not, see <http://www.gnu.org/licenses/>.
22 
23 
24  Please send bugreports with examples or suggestions to
25  jean-yves.didier__at__ibisc.univ-evry.fr
26 */
27 
28 
29 
66 #include <QCoreApplication>
67 #include <iostream>
68 #include <QXmlQuery>
69 #include <QXmlFormatter>
70 #include <QTextStream>
71 #include <QTemporaryFile>
72 #include <QDomDocument>
73 #include <QProcess>
74 #include <QStringList>
75 #include <QFile>
76 #include <QDir>
77 #include <QRegExp>
78 
79 
80 QString qmake_append =
81 "\n\nALXFILE = @\nOTHER_FILES += @\narcslibrary.output = alm_${QMAKE_FILE_BASE}.cpp\n" \
82 "arcslibrary.input = ALXFILE\narcslibrary.commands = arcslibmaker ${QMAKE_FILE_NAME}\n" \
83 "arcslibrary.variable_out = SOURCES\n" \
84 "QMAKE_EXTRA_COMPILERS += arcslibrary\n";
85 
86 QString qmake_preamble =
87  "INCLUDEPATH += $$(ARCSDIR)/include\n" \
88  "LIBS += -L$$(ARCSDIR)/lib -larcs\n "\
89  "CONFIG += dll\n";
90 
91 QStringList capList(QString pattern,QString input)
92 {
93  QStringList res;
94  QRegExp regexp(pattern);
95  regexp.setMinimal(true);
96 
97  int pos= 0;
98  while ((pos = regexp.indexIn(input, pos)) != -1) {
99  res << regexp.cap(1) ;
100  pos += regexp.matchedLength();
101  }
102  return res;
103 }
104 
105 
106 bool isLibraryFile(QString fileName)
107 {
108  QFile file(fileName);
109  if (!file.open(QIODevice::ReadOnly))
110  return false;
111  bool res = QTextStream(&file).readAll().contains(QRegExp("\\sMETALIB_BEGIN\\s"));
112  file.close();
113  return res;
114 }
115 
116 void processHeader(QString fileName)
117 {
118  //std::cout << "Processing " << qPrintable(fileName) << std::endl;
119  QFile input(fileName);
120  if (!input.open(QIODevice::ReadOnly))
121  {
122  std::cerr << "Failed to read " << qPrintable(fileName) << std::endl;
123  return ;
124  }
125  QString contents = QTextStream(&input).readAll();
126  input.close();
127 
128  QRegExp constructFilter("[(]\\s*QObject\\s*\\*\\s*(\\w*)\\s*=\\s*0\\s*,\\s*const\\s*char\\s*\\*\\s*(\\w*)\\s*=\\s*0\\s*[)]\\s*:\\s*(\\w*)\\s*[(]\\s*\\1\\s*,\\s*\\2\\s*[)]");
129  constructFilter.setMinimal(true);
130  int pos=0;
131 
132  bool res=false;
133  while ((pos = constructFilter.indexIn(contents,pos)) != -1 ) {
134  contents.replace(pos,constructFilter.matchedLength(), "(QObject* "+constructFilter.cap(1)
135  +"=0) : " + constructFilter.cap(3) + "(" + constructFilter.cap(1) + ")" );
136  res =true;
137  }
138 
139  constructFilter.setPattern("[(]\\s*QObject\\s*\\*\\s*(\\w*)\\s*=\\s*0\\s*,\\s*const\\s*char\\s*\\*\\s*(\\w*)\\s*=\\s*0\\s*[)]");
140  pos=0;
141 
142  while ((pos = constructFilter.indexIn(contents,pos)) != -1 ) {
143  contents.replace(pos,constructFilter.matchedLength(), "(QObject* "+constructFilter.cap(1)+"=0)");
144  res =true;
145  }
146 
147  if (res)
148  std::cout << "Modifying " << qPrintable(fileName) << std::endl;
149  else
150  return;
151 
152 
153  QFile output(fileName);
154  if (! output.open(QIODevice::WriteOnly))
155  {
156  std::cerr << "Failed to write " << qPrintable(fileName) << std::endl;
157  return ;
158  }
159  QTextStream(&output) << contents ;
160  output.close();
161 }
162 
163 
164 void processCppSource(QString fileName)
165 {
166  //std::cout << "Processing " << qPrintable(fileName) << std::endl;
167  QFile input(fileName);
168  if (!input.open(QIODevice::ReadOnly))
169  {
170  std::cerr << "Failed to read " << qPrintable(fileName) << std::endl;
171  return ;
172  }
173  QString contents = QTextStream(&input).readAll();
174  input.close();
175 
176  QRegExp constructFilter("[(]\\s*QObject\\s*\\*\\s*(\\w*)\\s*,\\s*const\\s*char\\s*\\*\\s*(\\w*)\\s*[)]\\s*:\\s*(\\w*)\\s*[(]\\s*\\1\\s*,\\s*\\2\\s*[)]");
177  constructFilter.setMinimal(true);
178  int pos=0;
179 
180  bool res=false;
181  while ((pos = constructFilter.indexIn(contents,pos)) != -1 ) {
182  contents.replace(pos,constructFilter.matchedLength(), "(QObject* "+constructFilter.cap(1)
183  +") : " + constructFilter.cap(3) + "(" + constructFilter.cap(1) + ")" );
184  res =true;
185  }
186  if (res)
187  std::cout << "Modifying " << qPrintable(fileName) << std::endl;
188  else
189  return;
190 
191 
192  QFile output(fileName);
193  if (! output.open(QIODevice::WriteOnly))
194  {
195  std::cerr << "Failed to write " << qPrintable(fileName) << std::endl;
196  return ;
197  }
198  QTextStream(&output) << contents ;
199  output.close();
200 }
201 
202 
203 void portLibraryFile(QString fileName,QString outName= QString::null)
204 {
205  QFile input(fileName);
206  if (!input.open(QIODevice::ReadOnly))
207  return ;
208  QString contents = QTextStream(&input).readAll();
209  input.close();
210 
211  QDomDocument doc;
212  QDomElement root = doc.createElement("library");
213  doc.appendChild(root);
214  QDomElement headers = doc.createElement("headers");
215  QDomElement components = doc.createElement("components");
216  root.appendChild(headers);
217  root.appendChild(components);
218 
219  // parsing headers
220  QRegExp regExp("#include\\s*\"(.+)\"");
221  regExp.setMinimal(true);
222  int pos = 0;
223  while ((pos = regExp.indexIn(contents, pos)) != -1) {
224  QDomElement header = doc.createElement("header");
225  header.setAttribute("name", regExp.cap(1));
226  headers.appendChild(header);
227  pos += regExp.matchedLength();
228  }
229 
230  //parsing components
231  regExp.setPattern("METALIB_OBJECT\\s*[(](.+)[)]");
232  pos = 0;
233  while ((pos = regExp.indexIn(contents, pos)) != -1) {
234  QDomElement component = doc.createElement("component");
235  component.setAttribute("name", regExp.cap(1));
236  components.appendChild(component);
237  pos += regExp.matchedLength();
238  }
239 
240  QFile* output;
241 
242  if (!outName.isEmpty())
243  {
244  output=new QFile(outName);
245  if (! output->open(QIODevice::WriteOnly) )
246  {
247  std::cerr << "Could not create ARCS2 libfile " << qPrintable(outName) << std::endl;
248  return ;
249  }
250  }
251  else
252  {
253  output = new QFile();
254  if(!output->open(1,QIODevice::WriteOnly))
255  {
256  std::cerr << "Could not open stdout ?!?" << std::endl;
257  return ;
258  }
259  }
260 
261  QTextStream ts(output);
262  doc.save(ts,4);
263  output->close();
264  delete(output);
265 }
266 
267 
268 void portApplicationFile(QString fileName)
269 {
270  QXmlQuery query;
271  QFileInfo fi(fileName);
272  QFile file(fi.baseName()+".arcs2."+fi.suffix());
273  query.bindVariable("file",QVariant(QDir::currentPath()+ QDir::separator()+fileName));
274  query.setQuery(QUrl("qrc:///arcsapp1to2.xq"),QDir::currentPath());
275  file.open(QIODevice::WriteOnly);
276  QXmlFormatter fmt(query, &file);
277  query.evaluateTo(&fmt);
278  file.close();
279 }
280 
281 
282 void portComponentFile(QString fileName)
283 {
284  QXmlQuery query;
285  QFileInfo fi(fileName);
286  QFile file(fi.baseName()+".arcs2."+fi.suffix());
287  query.bindVariable("file",QVariant(QDir::currentPath()+ QDir::separator()+fileName));
288  query.setQuery(QUrl("qrc:///arcscmp1to2.xq"),QDir::currentPath());
289  file.open(QIODevice::WriteOnly);
290  QXmlFormatter fmt(query, &file);
291  query.evaluateTo(&fmt);
292  file.close();
293 }
294 
295 
296 void portProjectFile(QString fileName)
297 {
298  std::cout << "Porting from Qt3 to Qt4" << std::endl;
299  std::cout << "============================================" << std::endl;
300 
301  // port Qt3 -> Qt4 à réaliser en premier
302 
303  // préparer le fichier temporaire pour faire le port
304  QTemporaryFile* portFile= QTemporaryFile::createLocalFile(":/q3porting_arcs.xml");
305  // Lancer le process qt3to4
306 
307  QProcess process;
308  process.setProcessChannelMode(QProcess::ForwardedChannels);;
309  QStringList args;
310  args << "-rulesFile" <<portFile->fileName() << "-alwaysOverwrite" << fileName ;
311  process.start("qt3to4",args );
312 
313  process.waitForFinished(-1);
314  delete portFile;
315 
316  std::cout << "============================================" << std::endl << std::endl;
317  std::cout << "Porting from ARCS1 to ARCS2" << std::endl;
318  std::cout << "============================================" << std::endl;
319 
320 
321  // a partir d'ici le port qt3 -> qt4 est théoriquement réalisé ...
322 
323  // couche spécifique ARCS2
324  QFile input(fileName);
325  if (!input.open(QIODevice::ReadOnly))
326  return ;
327  QString project = QTextStream(&input).readAll();
328 
329  input.close();
330 
331 
332  project.insert(0,"# Project modified by ARCS1to2\n\n");
333  // retrouver le nom de la cible.
334  QString target;
335  QRegExp regexp("\\bTARGET\\s*=\\s*(\\w+)\\b");
336  regexp.setMinimal(true);
337  int pos=regexp.indexIn(project);
338  if (pos > -1)
339  target= regexp.cap(1);
340  // sinon positionner target au nom de projet !
341  if (target.isEmpty())
342  target=QFileInfo(fileName).baseName();
343  std::cout << "Target is " << qPrintable(target) << std::endl;
344 
345 
346  // retrouver les fichiers cpp et surtout celui qui contient la définition de la bibliothèque.
347  QStringList cppSources=capList("\\s*(\\w+[.]cpp)\\s*",project);
348  for (int i=0; i < cppSources.count(); i++)
349  {
350  if( isLibraryFile(cppSources[i]))
351  {
352  std::cout << qPrintable(cppSources[i]) << " is the library file !"<< std::endl;
353  project.replace(QRegExp("\\b"+cppSources[i]+"\\b"),"");
354  QString alxFile = "lib"+QFileInfo(fileName).baseName()+".alx";
355  portLibraryFile(cppSources[i], alxFile);
356  project.append(qmake_append.replace("@",alxFile));
357  }
358  else
359  {
360  processCppSource(cppSources[i]);
361  }
362  }
363 
364  QStringList headers = capList("\\s*(\\w+[.]h)\\s*",project);
365  for (int i=0; i< headers.count(); i++)
366  {
367  processHeader(headers[i]);
368  }
369 
370  project.append(qmake_preamble);
371 
372  std::cout << "============================================" << std::endl << std::endl;
373  std::cout << "Writing down final project file" << std::endl;
374 
375  QFile output(fileName);
376  if (!output.open(QIODevice::WriteOnly))
377  {
378  std::cerr << "Cannot write down final project file " << std::endl;
379  return ;
380  }
381 
382  QTextStream(&output) << project ;
383  output.close();
384 
385 // std::cout << qPrintable(project) << std::endl;
386 }
387 
388 
389 
390 
391 bool parseOptions(int argc,char* argv[])
392 {
393  for (int i=1; i < argc; i++)
394  {
395  QString current(argv[i]);
396  if (current == "-a")
397  portApplicationFile(argv[++i]);
398  else
399  {
400  if(current== "-c")
401  portComponentFile(argv[++i]);
402  else
403  {
404  if (current == "-l")
405  portLibraryFile(argv[++i]);
406  else
407  if (current == "-p")
408  portProjectFile(argv[++i]);
409  }
410  }
411 
412  }
413  return true;
414 }
415 
416 
417 
418 void usage(char* name)
419 {
420  std::cout << "Usage: " << name << " (-a|-c|-l|-p) filename " << std::endl;
421  std::cout << "\t -a stands for application files in XML format;" << std::endl;
422  std::cout << "\t -c stands for composite component files in XML format;" << std::endl;
423  std::cout << "\t -l stands for library files in CPP format;" << std::endl;
424  std::cout << "\t -p stands for project files in pro format;" << std::endl;
425 }
426 
427 
428 
429 int main(int argc,char* argv[])
430 {
431  QCoreApplication app(argc,argv);
432  std::cout << argv[0] << ", a porting tool from ARCS1 to ARCS2" << std::endl;
433 
434  if (argc != 3)
435  {
436  usage(argv[0]);
437  return 0;
438  }
439 
440  if (!parseOptions(argc,argv))
441  {
442  usage(argv[0]);
443  return 0;
444  }
445 
446 
447  return 0;
448 }