Compare commits

...

4 Commits
v0.1 ... master

Author SHA1 Message Date
Julian Daube 8eacef2857 add readme 2017-10-09 21:12:16 +02:00
Julian Daube 640046ee1e fix target name not written when not overridden 2017-10-09 14:35:53 +02:00
Julian Daube 98b44ec923 add options to command 2017-10-09 13:56:48 +02:00
Julian Daube 4382ad6d59 cleanup (a bit) 2017-10-09 11:41:14 +02:00
3 changed files with 155 additions and 102 deletions

52
README.md Normal file
View File

@ -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
View File

@ -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 &current, 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;
}
}

View File

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