⛏️ index : buildtools.git

author Adrien Destugues <pulkomandy@pulkomandy.tk> 2020-09-22 20:29:49.0 +02:00:00
committer Adrien Destugues <pulkomandy@gmail.com> 2020-09-28 7:08:15.0 +00:00:00
commit
c8ecfce34c4db7b94581e365377aea88e862d00f [patch]
tree
2964b20ce0dd68dddfa65cf1545593986e1a57d3
parent
51dc302367154c517724d78f03d61abbe7802a1d
download
c8ecfce34c4db7b94581e365377aea88e862d00f.tar.gz

jam: add an option to generate compile_commands.json

Based on Boost Jam patch: https://github.com/boostorg/build/pull/133
retrofitted to our version. Only the rules whose name contains Cc or C++
are stored there. This can be improved if it's not good enough.

The commands are generated only as they are run. Unfortunately I think
with Jam there isn't really a way to do otherwise.

Change-Id: Ic5d44dc27baa2a2e4157324f6c5a228ab0366afe
Reviewed-on: https://review.haiku-os.org/c/buildtools/+/3260
Reviewed-by: Adrien Destugues <pulkomandy@gmail.com>

Diff

 jam/jam.c   | 22 ++++++++++++++++++++++
 jam/jam.h   |  1 +
 jam/make1.c | 89 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 111 insertions(+), 1 deletion(-)

diff --git a/jam/jam.c b/jam/jam.c
index 2bbf1ae..4dfb4c4 100644
--- a/jam/jam.c
+++ a/jam/jam.c
@@ -141,6 +141,7 @@
# else
	{ 0, 1 }, 		/* display actions  */
# endif
	0,			/* output compilation db here */
	0			/* output commands, not run them */
} ;

@@ -182,7 +183,7 @@

	argc--, argv++;

	if( ( n = getoptions( argc, argv, "d:j:f:gs:t:ano:qv", optv ) ) < 0 )
	if( ( n = getoptions( argc, argv, "d:j:f:gs:t:ano:cqv", optv ) ) < 0 )
	{
	    printf( "\nusage: jam [ options ] targets...\n\n" );

@@ -197,6 +198,7 @@
            printf( "-jx     Run up to x shell commands concurrently.\n" );
            printf( "-n      Don't actually execute the updating actions.\n" );
            printf( "-ox     Write the updating actions to file x.\n" );
            printf( "-c      Output JSON compilation database to compile_commands.json.\n" );
            printf( "-q      Quit quickly as soon as a target fails.\n" );
	    printf( "-sx=y   Set variable x=y, overriding environment.\n" );
            printf( "-tx     Rebuild x, even if it is up-to-date.\n" );
@@ -279,6 +281,17 @@
	    }
	}

	/* If we're asked to produce a compilation database, open the file. */
	if ( ( s = getoptval( optv, 'c', 0 ) ) )
	{
	    if ( !( globs.comp_db = fopen( "compile_commands.json", "w" ) ) )
	    {
		printf( "Failed to write to 'compile_commands.json'\n");
		exit( EXITBAD );
	    }
	    fprintf(globs.comp_db, "[\n");
	}

	/* Set JAMDATE first */

	{
@@ -438,6 +451,13 @@

	if( globs.cmdout )
	    fclose( globs.cmdout );

	/* close compilation database output file */
	if ( globs.comp_db )
	{
		fprintf(globs.comp_db, "]\n");
		fclose( globs.comp_db );
	}

	return status ? EXITBAD : EXITOK;
}
diff --git a/jam/jam.h b/jam/jam.h
index da3db48..6e509d5 100644
--- a/jam/jam.h
+++ a/jam/jam.h
@@ -500,6 +500,7 @@
	int	quitquick;
	int	newestfirst;		/* build newest sources first */
	char	debug[DEBUG_MAX];
	FILE	*comp_db;			/* output compilation db here */
	FILE	*cmdout;		/* print cmds, not run them */
} ;

diff --git a/jam/make1.c b/jam/make1.c
index ffb7831..906b41a 100644
--- a/jam/make1.c
+++ a/jam/make1.c
@@ -62,6 +62,8 @@
# include "command.h"
# include "execcmd.h"

#include <unistd.h>

static void make1a( TARGET *t, TARGET *parent );
static void make1b( TARGET *t );
static void make1c( TARGET *t );
@@ -73,6 +75,12 @@
static SETTINGS *make1settings( LIST *vars );
static void make1bind( TARGET *t, int warn );

void out_compile_database(
     char const * const action,
     char const * const source,
     char const * const command
 );

/* Ugly static - it's too hard to carry it through the callbacks. */

static struct {
@@ -294,6 +302,17 @@

	    if( globs.cmdout )
		fprintf( globs.cmdout, "%s", cmd->buf );

	    if ( globs.comp_db != NULL )
	    {
		const char* rule_name = cmd->rule->name;
		const char* target_name = lol_get( (LOL *)&cmd->args, 0 )->string;
		const char* source_name = NULL;
		LIST* sources = lol_get( (LOL *)&cmd->args, 1);
		if (sources != NULL)
		    source_name = lol_get((LOL *)&cmd->args, 1 )->string;
		out_compile_database( rule_name, source_name, cmd->buf );
	    }

	    if( globs.noexec )
	    {
@@ -670,4 +689,74 @@
	t->boundname = search( t->name, &t->time );
	t->binding = t->time ? T_BIND_EXISTS : T_BIND_MISSING;
	popsettings( t->settings );
}


static void out_json(char const* str, FILE* f)
{
     char const* escape_src = "\"\\\b\n\r\t";
     char const* escape_subst[] = {
         "\\\"", "\\\\", "\\b", "\\n", "\\r", "\\t"
     };
     char buffer[1024];
     int i = 0;

     /* trim leading whitespace */
     while (*str != 0 && strchr(" \t\n\r\t", *str) != NULL)
        ++str;

     for (; *str != 0; ++str)
     {
         char const* ch;
         char const* subst;
         if (i >= sizeof(buffer) - 10)
         {
             buffer[i] = 0;
             fputs(buffer, f);
             i = 0;
         }

         /* skip non-printable characters */
         if ((unsigned)*str < ' ') continue;

         ch = strchr(escape_src, *str);
         if (ch == NULL)
         {
             buffer[i++] = *str;
             continue;
         }
         subst = escape_subst[ch - escape_src];
         strcpy(&buffer[i], subst);
         i += strlen(subst);
     }

     buffer[i] = 0;
     fputs(buffer, f);
}


void out_compile_database
(
     char const * const action,
     char const * const source,
     char const * const command
)
{
     /* file format defined here:
      * http://clang.llvm.org/docs/JSONCompilationDatabase.html
      * we're not interested in link, mkdir, rm or any non-compile action
      */
     if (source
        && (strstr(action, "Cc") != NULL || strstr(action, "C++") != NULL))
     {
         char buffer[PATH_MAX];
         fputs("{ \"directory\": \"", globs.comp_db);
         out_json(getcwd(buffer, sizeof(buffer)), globs.comp_db);
         fputs("\", \"command\": \"", globs.comp_db);
         out_json(command, globs.comp_db);
         fputs("\", \"file\": \"", globs.comp_db);
         out_json(source, globs.comp_db);
         fputs("\" },\n", globs.comp_db);
     }

}