Compare commits
4 Commits
Author | SHA1 | Date |
---|---|---|
Julian Daube | 8eacef2857 | |
Julian Daube | 640046ee1e | |
Julian Daube | 98b44ec923 | |
Julian Daube | 4382ad6d59 |
|
@ -0,0 +1,52 @@
|
|||
## Description
|
||||
texdepends is a small tool that scans given latex files for the following macros:
|
||||
- \input{}
|
||||
- \include{}
|
||||
- \lstinputlisting{}
|
||||
and lists all the files included using these macros in a file with either a given name
|
||||
or with the name of the processed file and a .d extension, using makefile dependency syntax.
|
||||
|
||||
This means for a file that uses e.g. `\input{Intro.tex}` somewhere (the file is named `document.tex`},
|
||||
a call to `texdepends document.tex` will generate a file called `document.d` with the following content:
|
||||
```makefile
|
||||
document.tex: Intro.tex\
|
||||
```
|
||||
|
||||
This file can be used to trigger a rebuild using make of the document if there were changes in files affecting it.
|
||||
|
||||
## Command line options
|
||||
|
||||
There are two possible switches:
|
||||
|
||||
| Name | Description |
|
||||
| --:|:-- |
|
||||
| --target | |
|
||||
| -t | Can be used to set the string output before the colon char |
|
||||
| | |
|
||||
| --output: | |
|
||||
| -o: | modifies the behaviour so all file dependencies of all input files will be written to the given file, instead of creating one file per input. |
|
||||
|
||||
|
||||
**Note: The --output switch should be used in combination with --target, because otherwise no target will be present in the output file!**
|
||||
|
||||
## Building
|
||||
The Program is currently only written for Linux, i will see if a windows port is feasible (should be straight forward).
|
||||
|
||||
### Linux
|
||||
Under Linux, a call to `make` should build produce a binary called `texdepends`.
|
||||
A simple test can be performed using `make test`
|
||||
|
||||
## Notes
|
||||
|
||||
- The program will follow all found *.tex links and parse them too, if possible. Files included in included files will therefore also show up as dependencies. It will only parse a file once
|
||||
- This is no latex parser, it has two major drawbacks:
|
||||
- No Parsing of `\if`-directives, which means, that files included via `\if` will always show up
|
||||
- No parsing of arguments to macros. The Only macro-supsitution happens inside `\include` or `\input` arguments. The only macro definitions supsituted are those made via `\def`. Therefore the following include will not show up in the output because it happend in a macro-argument.
|
||||
|
||||
```
|
||||
\frame{
|
||||
\include{Intro.tex}
|
||||
}
|
||||
```
|
||||
|
||||
|
204
main.cpp
204
main.cpp
|
@ -7,16 +7,17 @@
|
|||
|
||||
#include <sys/mman.h> // for mmap()
|
||||
#include <sys/stat.h> // for fstat()
|
||||
#include <fcntl.h> // for open()
|
||||
#include <fcntl.h> // for open(), O_RDONLY
|
||||
#include <unistd.h> // for close()
|
||||
#include <errno.h> // for perror()
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
#include <cstring>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <functional>
|
||||
|
||||
#include "fs.hpp"
|
||||
#include "memory_string.hpp"
|
||||
|
@ -60,8 +61,6 @@ std::string readTill(iterator &start, const iterator &end, std::function<bool(co
|
|||
|
||||
template <typename iterator>
|
||||
std::string readBrackets(iterator ¤t, const iterator &end, const char * brackets) {
|
||||
// auto current = begin;
|
||||
|
||||
if (current == end || *current != brackets[0]) {
|
||||
std::cout << brackets[0] << "!=" << *current;
|
||||
return std::string();
|
||||
|
@ -122,24 +121,9 @@ std::string InputExtractor::macroExpand(const std::string &input) {
|
|||
return result;
|
||||
}
|
||||
|
||||
#include <functional>
|
||||
|
||||
typedef std::map<std::string, std::function<void(InputExtractor::List&, std::string)>> CommandList;
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
|
||||
#include <fstream>
|
||||
|
||||
bool Exists(std::string path) {
|
||||
std::ifstream file(path);
|
||||
if (!file) {
|
||||
return false;
|
||||
}
|
||||
|
||||
file.close();
|
||||
return true;
|
||||
}
|
||||
|
||||
InputExtractor::List InputExtractor::Include(Path::Path path) {
|
||||
path = Path::Clean(path);
|
||||
List list;
|
||||
|
@ -178,11 +162,13 @@ InputExtractor::List InputExtractor::Include(Path::Path path) {
|
|||
return list;
|
||||
}
|
||||
|
||||
// Substring str((const char *)memptr, (const char*)memptr + fileinfo.st_size);
|
||||
// create iteratable memory object
|
||||
MemoryString file((char*)memptr, fileinfo.st_size);
|
||||
|
||||
std::string basedir = Path::Dir(path);
|
||||
list = (*this)(basedir, file); // follow include
|
||||
|
||||
// evaluate memory region (aka the file)
|
||||
list = (*this)(basedir, file);
|
||||
|
||||
// cleanup
|
||||
munmap(memptr, fileinfo.st_size);
|
||||
|
@ -192,6 +178,8 @@ InputExtractor::List InputExtractor::Include(Path::Path path) {
|
|||
cout << path << "done" << endl;
|
||||
}
|
||||
|
||||
// make a relative filepath absolute using the root filename
|
||||
// and/or the process working directory
|
||||
Path::Path fixFilename(const Path::Path &file, const Path::Path &root) {
|
||||
Path::Path temp(file);
|
||||
|
||||
|
@ -208,7 +196,7 @@ Path::Path fixFilename(const Path::Path &file, const Path::Path &root) {
|
|||
return Path::Clean(temp);
|
||||
}
|
||||
|
||||
|
||||
// evaluate a tex file searching for input statements
|
||||
InputExtractor::List InputExtractor::operator()(const Path::Path &file, const MemoryString &str){
|
||||
List result;
|
||||
CommandList IncludeCommands;
|
||||
|
@ -292,104 +280,116 @@ InputExtractor::List InputExtractor::operator()(const Path::Path &file, const Me
|
|||
return result;
|
||||
}
|
||||
|
||||
struct Config {
|
||||
std::string targetName;
|
||||
Path::Path outfilePath;
|
||||
bool outfileOverride;
|
||||
} config ;
|
||||
|
||||
std::ostream &writeTarget(std::ostream &stream, const std::string &target_name) {
|
||||
stream << target_name << ":";
|
||||
return stream;
|
||||
}
|
||||
|
||||
std::ostream &writeTargetDepends(std::ostream &stream, const Path::Path &path) {
|
||||
stream << path << "\\\n";
|
||||
return stream;
|
||||
}
|
||||
|
||||
int openOutfile(std::ofstream &stream) {
|
||||
cout << "writing dependency rules to " << config.outfilePath << endl;
|
||||
|
||||
stream.close();
|
||||
stream.open(config.outfilePath);
|
||||
|
||||
if (!stream) {
|
||||
cerr << "could not write to " << config.outfilePath << endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!config.targetName.empty()) {
|
||||
writeTarget(stream, config.targetName);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#include <getopt.h>
|
||||
|
||||
struct option long_opts[] = {
|
||||
{"output", required_argument, NULL, 'o'},
|
||||
{"target", required_argument, NULL, 't'},
|
||||
{}, // terminator
|
||||
};
|
||||
|
||||
|
||||
void help(int argc, char ** args) {
|
||||
cout << args[0] << " [-o output] files..." << endl;
|
||||
cout << "--output" << endl;
|
||||
cout << "-o\tby default the program will create a depfile for every input" << endl;
|
||||
cout << "\tnaming it like the tex file with a .d extension. By giving the -o Option" << endl;
|
||||
cout << "\tthe output fill instead be put in this file exclusivly" << endl;
|
||||
}
|
||||
|
||||
int main(int argc, char ** args) {
|
||||
// find all the files the given tex files depend on
|
||||
int fd = 0;
|
||||
struct stat filestat;
|
||||
|
||||
int long_index = 0;
|
||||
int opt = 0;
|
||||
while((opt = getopt_long(argc, args, "o:t:", long_opts, &long_index)) != -1) {
|
||||
switch(opt) {
|
||||
case 'o':
|
||||
config.outfilePath = optarg;
|
||||
config.outfileOverride = true;
|
||||
break;
|
||||
case 't':
|
||||
config.targetName = optarg;
|
||||
break;
|
||||
case '?':
|
||||
help(argc, args);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
for(;argc > 1; --argc) {
|
||||
std::ofstream outfile;
|
||||
|
||||
Path::Path filename = args[argc-1];
|
||||
if (!config.outfilePath.empty() && !openOutfile(outfile)) {
|
||||
return -1; // failed to open outfile
|
||||
}
|
||||
|
||||
// scan remaining arguments as input filenames
|
||||
for(; optind < argc; optind++) {
|
||||
Path::Path filename = args[optind];
|
||||
InputExtractor parser;
|
||||
|
||||
// parse file
|
||||
InputExtractor::List list = parser.Include(filename);
|
||||
|
||||
// output results in makefile rule style
|
||||
|
||||
Path::Path outfile_name = Path::Basename(filename) + ".d";
|
||||
cout << "writing dependecy rules to " << outfile_name << "...";
|
||||
// check for open outfile
|
||||
if (!config.outfileOverride) {
|
||||
config.outfilePath = Path::Basename(filename) + ".d";
|
||||
|
||||
std::ofstream outfile(outfile_name);
|
||||
|
||||
if (!outfile) {
|
||||
cout << "could not create file!" << endl;
|
||||
continue;
|
||||
}
|
||||
|
||||
outfile << filename << ":";
|
||||
|
||||
for (auto it = list.begin(); it != list.end(); it++) {
|
||||
outfile << *it << "\t\\\n";
|
||||
}
|
||||
|
||||
cout << "done" << endl;
|
||||
|
||||
/*
|
||||
char * filename = args[argc-1];
|
||||
cout << "opening " << filename << "...";
|
||||
|
||||
// try to open file
|
||||
fd = open(filename, O_RDONLY);
|
||||
if (fd == -1) {
|
||||
perror("could not open input file");
|
||||
continue;
|
||||
}
|
||||
|
||||
fstat(fd, &filestat);
|
||||
//cout << "file size: " << filestat.st_size << endl;
|
||||
|
||||
// try to mmap file
|
||||
void * memory_area = mmap(NULL, filestat.st_size, PROT_READ, MAP_SHARED, fd, 0);
|
||||
if (memory_area == nullptr) {
|
||||
perror("could not mmap the input");
|
||||
continue;
|
||||
}
|
||||
|
||||
cout << "start parsing" << endl;
|
||||
|
||||
MemoryString file((char*)memory_area, filestat.st_size);
|
||||
|
||||
try {
|
||||
InputExtractor::List list = InputExtractor()(file);
|
||||
|
||||
Path::Path outfilename = Path::Basename(Path::Path(filename)) + ".d";
|
||||
|
||||
cout << "writing makedeps file to " << outfilename << "..." << endl;
|
||||
|
||||
// write in makefile style
|
||||
std::ofstream output(outfilename);
|
||||
if (!output) {
|
||||
std::cout << "could not create output file" << std::endl;
|
||||
} else {
|
||||
output << filename << ": ";
|
||||
|
||||
for (auto it = list.begin(); it != list.end(); it++) {
|
||||
if (Path::isRelative(*it)) {
|
||||
if (Path::isRelative(filename)) {
|
||||
*it = Path::Join(fs::cwd(), filename, *it);
|
||||
} else {
|
||||
*it = Path::Join(Path::Path(filename), *it);
|
||||
}
|
||||
}
|
||||
|
||||
cout << "depends: " << *it << endl;
|
||||
output << '\t' << *it << "\t\\\n";
|
||||
}
|
||||
output << endl;
|
||||
if (!openOutfile(outfile)) {
|
||||
continue; // just skip this file
|
||||
}
|
||||
|
||||
cout << filename << done;
|
||||
|
||||
} catch(InputExtractor::Exception &e) {
|
||||
cout << e.what() << endl;
|
||||
writeTarget(outfile, filename);
|
||||
}
|
||||
|
||||
// cleanup
|
||||
munmap(memory_area, filestat.st_size);
|
||||
close(fd);
|
||||
for (auto it = list.begin(); it != list.end(); it++) {
|
||||
writeTargetDepends(outfile, *it);
|
||||
}
|
||||
|
||||
*/
|
||||
// add newline for cleanness
|
||||
outfile << endl;
|
||||
|
||||
cout << "done" << endl;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue