main shinobi / backends / compdb.c
  1#include "compdb.h"
  2#include "internal.h"
  3
  4#include <stdio.h>
  5#include <stdlib.h>
  6#include <string.h>
  7#include <unistd.h>
  8
  9/* compile_commands.json backend */
 10
 11static void
 12emitjson(FILE *fp, const char *s)
 13{
 14	size_t i;
 15
 16	for (i = 0; s[i]; i++) {
 17		switch (s[i]) {
 18		case '"':
 19		case '\\':
 20			fputc('\\', fp);
 21			fputc(s[i], fp);
 22			break;
 23		case '\n':
 24			fputs("\\n", fp);
 25			break;
 26		case '\r':
 27			fputs("\\r", fp);
 28			break;
 29		case '\t':
 30			fputs("\\t", fp);
 31			break;
 32		default:
 33			fputc(s[i], fp);
 34			break;
 35		}
 36	}
 37}
 38
 39static int
 40hassuffix(const char *s, const char *suffix)
 41{
 42	size_t ns, nfx;
 43
 44	ns = strlen(s);
 45	nfx = strlen(suffix);
 46	return ns >= nfx && strcmp(s + ns - nfx, suffix) == 0;
 47}
 48
 49static int
 50issource(const char *path)
 51{
 52	static const char *const exts[] = {
 53	    ".c", ".cc", ".cpp", ".cxx", ".c++", ".m", ".mm", 0};
 54	size_t i;
 55
 56	for (i = 0; exts[i]; i++) {
 57		if (hassuffix(path, exts[i]))
 58			return 1;
 59	}
 60	return 0;
 61}
 62
 63static int
 64iscompile(const struct Target *t)
 65{
 66	const char *src;
 67
 68	if (t->recipes.n != 1 || totalprereqs(t) != 1)
 69		return 0;
 70	src = firstprereq(t);
 71	if (!src || !issource(src))
 72		return 0;
 73	if (strstr(t->recipes.v[0].body, " -c ") || strstr(t->recipes.v[0].body, "\t-c ") ||
 74	    strstr(t->recipes.v[0].body, " -c\t") || hassuffix(t->recipes.v[0].body, " -c"))
 75		return 1;
 76	return strstr(t->recipes.v[0].body, "$<") != 0;
 77}
 78
 79int
 80gencompdb(const struct Graph *graph, const char *path)
 81{
 82	FILE *fp;
 83	size_t i;
 84	int first;
 85	char cwd[4096];
 86
 87	fp = fopen(path, "w");
 88	if (!fp)
 89		return -1;
 90	if (!getcwd(cwd, sizeof(cwd))) {
 91		fclose(fp);
 92		return -1;
 93	}
 94
 95	fprintf(fp, "[\n");
 96	first = 1;
 97	for (i = 0; i < graph->n; i++) {
 98		if (!iscompile(&graph->v[i]))
 99			continue;
100		if (!first)
101			fprintf(fp, ",\n");
102		first = 0;
103		fprintf(fp, "  {\n");
104		fprintf(fp, "    \"directory\": \"");
105		emitjson(fp, cwd);
106		fprintf(fp, "\",\n");
107		fprintf(fp, "    \"command\": \"");
108		emitjson(fp, graph->v[i].recipes.v[0].body);
109		fprintf(fp, "\",\n");
110		fprintf(fp, "    \"file\": \"");
111		emitjson(fp, firstprereq(&graph->v[i]));
112		fprintf(fp, "\",\n");
113		fprintf(fp, "    \"output\": \"");
114		emitjson(fp, graph->v[i].name);
115		fprintf(fp, "\"\n");
116		fprintf(fp, "  }");
117	}
118	fprintf(fp, "\n]\n");
119	fclose(fp);
120	return 0;
121}