Compare commits
No commits in common. "master" and "v0.1" have entirely different histories.
52
README.md
52
README.md
@ -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
200
main.cpp
@ -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 ¤t, const iterator &end, const char * brackets) {
|
std::string readBrackets(iterator ¤t, 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);
|
||||||
|
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user