Compare commits

..

No commits in common. "master" and "v0.1" have entirely different histories.
master ... v0.1

3 changed files with 100 additions and 153 deletions

View File

@ -1,52 +0,0 @@
## 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}
}
```

200
main.cpp
View File

@ -7,17 +7,16 @@
#include <sys/mman.h> // for mmap() #include <sys/mman.h> // for mmap()
#include <sys/stat.h> // for fstat() #include <sys/stat.h> // for fstat()
#include <fcntl.h> // for open(), O_RDONLY #include <fcntl.h> // for open()
#include <unistd.h> // for close() #include <unistd.h> // for close()
#include <errno.h> // for perror() #include <errno.h> // for perror()
#include <iostream> #include <iostream>
#include <fstream>
#include <string> #include <string>
#include <cstring>
#include <vector> #include <vector>
#include <map> #include <map>
#include <set> #include <set>
#include <functional>
#include "fs.hpp" #include "fs.hpp"
#include "memory_string.hpp" #include "memory_string.hpp"
@ -61,6 +60,8 @@ std::string readTill(iterator &start, const iterator &end, std::function<bool(co
template <typename iterator> template <typename iterator>
std::string readBrackets(iterator &current, const iterator &end, const char * brackets) { std::string readBrackets(iterator &current, const iterator &end, const char * brackets) {
// auto current = begin;
if (current == end || *current != brackets[0]) { if (current == end || *current != brackets[0]) {
std::cout << brackets[0] << "!=" << *current; std::cout << brackets[0] << "!=" << *current;
return std::string(); return std::string();
@ -121,9 +122,24 @@ std::string InputExtractor::macroExpand(const std::string &input) {
return result; return result;
} }
#include <functional>
typedef std::map<std::string, std::function<void(InputExtractor::List&, std::string)>> CommandList; 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) { InputExtractor::List InputExtractor::Include(Path::Path path) {
path = Path::Clean(path); path = Path::Clean(path);
List list; List list;
@ -162,13 +178,11 @@ InputExtractor::List InputExtractor::Include(Path::Path path) {
return list; return list;
} }
// create iteratable memory object // Substring str((const char *)memptr, (const char*)memptr + fileinfo.st_size);
MemoryString file((char*)memptr, fileinfo.st_size); MemoryString file((char*)memptr, fileinfo.st_size);
std::string basedir = Path::Dir(path); std::string basedir = Path::Dir(path);
list = (*this)(basedir, file); // follow include
// evaluate memory region (aka the file)
list = (*this)(basedir, file);
// cleanup // cleanup
munmap(memptr, fileinfo.st_size); munmap(memptr, fileinfo.st_size);
@ -178,8 +192,6 @@ InputExtractor::List InputExtractor::Include(Path::Path path) {
cout << path << "done" << endl; 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 fixFilename(const Path::Path &file, const Path::Path &root) {
Path::Path temp(file); Path::Path temp(file);
@ -196,7 +208,7 @@ Path::Path fixFilename(const Path::Path &file, const Path::Path &root) {
return Path::Clean(temp); return Path::Clean(temp);
} }
// evaluate a tex file searching for input statements
InputExtractor::List InputExtractor::operator()(const Path::Path &file, const MemoryString &str){ InputExtractor::List InputExtractor::operator()(const Path::Path &file, const MemoryString &str){
List result; List result;
CommandList IncludeCommands; CommandList IncludeCommands;
@ -280,116 +292,104 @@ InputExtractor::List InputExtractor::operator()(const Path::Path &file, const Me
return result; 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) { int main(int argc, char ** args) {
// find all the files the given tex files depend on // find all the files the given tex files depend on
int fd = 0; int fd = 0;
struct stat filestat; 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;
}
}
std::ofstream outfile; for(;argc > 1; --argc) {
if (!config.outfilePath.empty() && !openOutfile(outfile)) { Path::Path filename = args[argc-1];
return -1; // failed to open outfile
}
// scan remaining arguments as input filenames
for(; optind < argc; optind++) {
Path::Path filename = args[optind];
InputExtractor parser; InputExtractor parser;
// parse file
InputExtractor::List list = parser.Include(filename); InputExtractor::List list = parser.Include(filename);
// output results in makefile rule style // output results in makefile rule style
// check for open outfile Path::Path outfile_name = Path::Basename(filename) + ".d";
if (!config.outfileOverride) { cout << "writing dependecy rules to " << outfile_name << "...";
config.outfilePath = Path::Basename(filename) + ".d";
if (!openOutfile(outfile)) { std::ofstream outfile(outfile_name);
continue; // just skip this file
if (!outfile) {
cout << "could not create file!" << endl;
continue;
} }
writeTarget(outfile, filename); outfile << filename << ":";
}
for (auto it = list.begin(); it != list.end(); it++) { for (auto it = list.begin(); it != list.end(); it++) {
writeTargetDepends(outfile, *it); outfile << *it << "\t\\\n";
} }
// add newline for cleanness
outfile << endl;
cout << "done" << endl; 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;
}
cout << filename << done;
} catch(InputExtractor::Exception &e) {
cout << e.what() << endl;
}
// cleanup
munmap(memory_area, filestat.st_size);
close(fd);
*/
} }
} }

View File

@ -1,7 +1,6 @@
\documentclass{article} \documentclass{article}
\usepackage[ngerman]{babel} \usepackage[ngerman]{babel}
\def\test{}
\author{Julian Daube} \author{Julian Daube}
\title{Ein Test} \title{Ein Test}