~ubuntu-branches/ubuntu/wily/gnome-build/wily

« back to all changes in this revision

Viewing changes to src/backends/libgbf_mkfile/gbf-mkfile-build.c

  • Committer: Bazaar Package Importer
  • Author(s): Rob Bradford
  • Date: 2008-09-24 22:23:00 UTC
  • mfrom: (1.1.10 upstream)
  • Revision ID: james.westby@ubuntu.com-20080924222300-td15smr2fr5fsfjd
Tags: 2.24.0-1
New upstream release

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/* gbf-mkfile-build.c
2
 
 *
3
 
 * Copyright (C) 2005  Eric Greveson
4
 
 *
5
 
 * This program is free software; you can redistribute it and/or
6
 
 * modify it under the terms of the GNU General Public License as
7
 
 * published by the Free Software Foundation; either version 2 of the
8
 
 * License, or (at your option) any later version.
9
 
 *
10
 
 * This program is distributed in the hope that it will be useful,
11
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13
 
 * General Public License for more details.
14
 
 *
15
 
 * You should have received a copy of the GNU General Public
16
 
 * License along with this program; if not, write to the
17
 
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18
 
 * Boston, MA 02111-1307, USA.
19
 
 *
20
 
 * Author: Eric Greveson
21
 
 * Based on the Autotools GBF backend (libgbf-am) by
22
 
 *   JP Rosevear
23
 
 *   Dave Camp
24
 
 *   Naba Kumar
25
 
 *   Gustavo Giráldez
26
 
 */
27
 
 
28
 
#ifdef HAVE_CONFIG_H
29
 
#include <config.h>
30
 
#endif
31
 
 
32
 
#include <stdlib.h>
33
 
#include <unistd.h>
34
 
#include <string.h>
35
 
#include <regex.h>
36
 
#include "gbf-i18n.h"
37
 
#include "gbf-mkfile-build.h"
38
 
 
39
 
#ifdef NATIVE_GNU_REGEX
40
 
 
41
 
typedef struct {
42
 
        GbfMkfileProject *project;
43
 
        int id;
44
 
        int num_channels;
45
 
        GList *callbacks;
46
 
 
47
 
        /* Regex structures. */
48
 
        struct re_pattern_buffer dir_buf;
49
 
        struct re_pattern_buffer warn_buf;
50
 
        struct re_pattern_buffer err_buf;
51
 
        struct re_registers reg;
52
 
 
53
 
        /* Build info. */
54
 
        char *build_dir;
55
 
} BuildInfo;
56
 
 
57
 
 
58
 
static void
59
 
build_info_free (BuildInfo *info)
60
 
{
61
 
        g_assert (info);
62
 
        
63
 
        if (info->build_dir)
64
 
                g_free (info->build_dir);
65
 
        if (info->dir_buf.fastmap)
66
 
                g_free (info->dir_buf.fastmap);
67
 
        if (info->warn_buf.fastmap)
68
 
                g_free (info->warn_buf.fastmap);
69
 
        if (info->err_buf.fastmap)
70
 
                g_free (info->err_buf.fastmap);
71
 
        g_free (info);
72
 
}
73
 
 
74
 
static void
75
 
build_msg (BuildInfo      *info,
76
 
           GbfBuildMessage type,
77
 
           gpointer        msg)
78
 
{
79
 
        GList *l;
80
 
 
81
 
        for (l = info->callbacks; l; l = l->next) {
82
 
                GbfMkfileBuildCallback *cb = l->data;
83
 
                (* cb->callback) (GBF_PROJECT (info->project),
84
 
                                  type, msg,
85
 
                                  cb->user_data);
86
 
        }
87
 
}
88
 
 
89
 
static void
90
 
parse_output (BuildInfo  *info,
91
 
              const char *line)
92
 
{
93
 
        int line_length = strlen (line);
94
 
 
95
 
        /* Check for directory changes. */
96
 
        if (re_search (&info->dir_buf, line, line_length, 0,
97
 
                       line_length, &info->reg) != -1) {
98
 
                if (info->reg.num_regs >= 2) {
99
 
                        if (info->build_dir)
100
 
                                g_free (info->build_dir);
101
 
                        info->build_dir = g_strndup (line + info->reg.start[1],
102
 
                                                     info->reg.end[1] - info->reg.start[1]);
103
 
                }
104
 
        }
105
 
 
106
 
        /* Check for warnings & errors. */
107
 
        if (re_search (&info->warn_buf, line, line_length, 0,
108
 
                       line_length, &info->reg) != -1) {
109
 
                GbfBuildWarning *warn;
110
 
                char *text;
111
 
 
112
 
                /* Create new warning. */
113
 
                warn = g_new0 (GbfBuildWarning, 1);
114
 
                text = g_strndup (line + info->reg.start[1],
115
 
                                  info->reg.end[1] - info->reg.start[1]);
116
 
                if (text[0] != '/') { /* only prepend build_dir if path not absolute */
117
 
                        warn->filename = g_strconcat (info->build_dir, "/", text, NULL);
118
 
                        g_free (text);
119
 
                } else {
120
 
                        warn->filename = text;
121
 
                }
122
 
 
123
 
                text = g_strndup (line + info->reg.start[2],
124
 
                                  info->reg.end[2] - info->reg.start[2]);
125
 
                warn->line = atoi (text);
126
 
                g_free (text);
127
 
                warn->warning = g_strndup (line + info->reg.start[3],
128
 
                                           info->reg.end[3] - info->reg.start[3]);
129
 
                warn->output = g_strdup (line);
130
 
 
131
 
                build_msg (info, GBF_BUILD_WARNING, warn);
132
 
                /* FIXME: We should free warn here, and make copy in gbf-build-info.c. */
133
 
        } else if (re_search (&info->err_buf, line, line_length, 0,
134
 
                              line_length, &info->reg) != -1) {
135
 
                GbfBuildError *err;
136
 
                char *text;
137
 
 
138
 
                /* Create new error. */
139
 
                err = g_new0 (GbfBuildError, 1);
140
 
                text = g_strndup (line + info->reg.start[1],
141
 
                                  info->reg.end[1] - info->reg.start[1]);
142
 
                if (text[0] != '/') { /* only prepend build_dir if path not absolute */
143
 
                        err->filename = g_strconcat (info->build_dir, "/", text, NULL);
144
 
                        g_free (text);
145
 
                } else {
146
 
                        err->filename = text;
147
 
                }
148
 
 
149
 
                text = g_strndup (line + info->reg.start[2],
150
 
                                  info->reg.end[2] - info->reg.start[2]);
151
 
                err->line = atoi (text);
152
 
                g_free (text);
153
 
                err->error = g_strndup (line + info->reg.start[3],
154
 
                                        info->reg.end[3] - info->reg.start[3]);
155
 
                err->output = g_strdup (line);
156
 
 
157
 
                build_msg (info, GBF_BUILD_ERROR, err);
158
 
                /* FIXME: We should free err here, and make copy in gbf-build-info.c. */
159
 
        } else {
160
 
                build_msg (info, GBF_BUILD_OUTPUT, (gpointer)line);
161
 
        }
162
 
}
163
 
 
164
 
static gboolean
165
 
build_output_cb (GIOChannel  *chan,
166
 
                 GIOCondition cond,
167
 
                 gpointer     user_data)
168
 
{
169
 
        BuildInfo *info = user_data;
170
 
        char *line;
171
 
        gsize len;
172
 
        gsize term;
173
 
        GError *err = NULL;
174
 
        GIOStatus status;
175
 
 
176
 
        status = g_io_channel_read_line (chan, &line, &len, &term, &err);
177
 
        if (status != G_IO_STATUS_NORMAL || line == NULL || err != NULL) {
178
 
                if (err) {
179
 
                        g_warning ("Error reading io channel: %s", err->message);
180
 
                        g_error_free (err);
181
 
                }
182
 
                info->num_channels--;
183
 
                if (info->num_channels == 0) {
184
 
                        build_msg (info, GBF_BUILD_END, _("Build ended"));
185
 
 
186
 
                        g_signal_emit_by_name (G_OBJECT (info->project),
187
 
                                               "build_stop", TRUE);
188
 
 
189
 
                        build_info_free (info);
190
 
                }
191
 
 
192
 
                return FALSE;
193
 
        }
194
 
 
195
 
        parse_output (info, line);
196
 
        g_free (line);
197
 
 
198
 
        return TRUE;
199
 
}
200
 
 
201
 
static gboolean
202
 
compile_pattern (struct re_pattern_buffer *buf,
203
 
                 const char               *pattern)
204
 
{
205
 
        memset (buf, 0, sizeof (struct re_pattern_buffer));
206
 
        buf->translate = NULL;
207
 
        buf->fastmap = g_malloc (256);
208
 
        buf->allocated = 0;
209
 
        buf->buffer = NULL;
210
 
        buf->can_be_null = 0;
211
 
        buf->no_sub = 0;
212
 
 
213
 
        if (!re_compile_pattern (pattern, strlen (pattern), buf)) {
214
 
                if (re_compile_fastmap (buf) != 0) {
215
 
                        g_warning ("IMPORTANT REGEX FAILED TO CREASTE FASTMAP");
216
 
                        g_free (buf->fastmap);
217
 
                        buf->fastmap = NULL;
218
 
                }
219
 
        } else {
220
 
                g_warning ("IMPORTANT REGEX FAILED TO COMPILE");
221
 
                return FALSE;
222
 
        }
223
 
 
224
 
        return TRUE;
225
 
}
226
 
 
227
 
int
228
 
gbf_build_run (GbfMkfileProject    *project,
229
 
               gchar           *id,
230
 
               const char      *project_dir,
231
 
               GList           *callbacks)
232
 
{
233
 
        static const char *dir_regex = "Entering directory `([^']+)'";
234
 
        static const char *warn_regex = "^([^:]+):([0-9]+): warning: (.+)$";
235
 
        static const char *err_regex = "^([^:]+):([0-9]+): (.+)$";
236
 
 
237
 
        static int buildid = 0;
238
 
        char *argv[5];
239
 
        char *build_dir;
240
 
        BuildInfo *info;
241
 
        char *tmp, *msg;
242
 
        int output, err, pid;
243
 
        GIOChannel *out_channel, *err_channel;
244
 
        reg_syntax_t old_options;
245
 
        GError *error = NULL;
246
 
        const char *charset;
247
 
        GNode *g_node;
248
 
        GbfMkfileNode *node;
249
 
 
250
 
        /* default targets  */
251
 
        if ( 0 == strcmp (id, GBF_MKFILE_BUILD_ID_ALL) ||
252
 
             0 == strcmp (id, GBF_BUILD_ID_DEFAULT) ) {
253
 
                argv[0] = g_strdup (project->make_command);
254
 
                argv[1] = g_strdup ("all");
255
 
                argv[2] = NULL;
256
 
                build_dir = g_strdup (project_dir);
257
 
        } else if ( 0 == strcmp (id, GBF_MKFILE_BUILD_ID_CLEAN) ) {
258
 
                argv[0] = g_strdup (project->make_command);
259
 
                argv[1] = g_strdup ("clean");
260
 
                argv[2] = NULL;
261
 
                build_dir = g_strdup (project_dir);
262
 
        } else if ( 0 == strcmp (id, GBF_MKFILE_BUILD_ID_INSTALL) ) {
263
 
                argv[0] = g_strdup (project->make_command);
264
 
                argv[1] = g_strdup ("install");
265
 
                argv[2] = NULL;
266
 
                build_dir = g_strdup (project_dir);
267
 
        } else { 
268
 
                /* user's target */
269
 
                /* id is like: "USER:real_id"  */
270
 
                g_node = g_hash_table_lookup (project->targets, id+strlen (GBF_MKFILE_BUILD_ID_USER_PREFIX) );
271
 
                if (!g_node) {
272
 
                        g_warning ("Invalid build: %s", id);
273
 
                        return -1;
274
 
                }
275
 
                node = GBF_MKFILE_NODE (g_node);
276
 
        
277
 
                if ( 0 == strcmp (node->detail, "program") ||
278
 
                     0 == strcmp (node->detail, "static_lib") ||
279
 
                     0 == strcmp (node->detail, "shared_lib") ) {
280
 
                        /* find the right build dir and make argument */
281
 
                        /* FIXME: durty ?? */
282
 
                        gchar *cur, *last;
283
 
                        cur = id+strlen (GBF_MKFILE_BUILD_ID_USER_PREFIX);
284
 
                        last = cur;
285
 
                        while( *cur != '\0' && *cur != ':') {
286
 
                                if ('/' == *cur) {
287
 
                                        last = cur;
288
 
                                }
289
 
                                cur++;
290
 
                        }
291
 
                        *last = '\0';
292
 
                        *cur = '\0';
293
 
                        build_dir = g_strdup_printf ("%s%s", project_dir,
294
 
                                        id+strlen (GBF_MKFILE_BUILD_ID_USER_PREFIX));
295
 
 
296
 
                        argv[0] = g_strdup (project->make_command);
297
 
                        argv[1] = g_strdup (last+1);
298
 
                        argv[2] = NULL;
299
 
                } else {
300
 
                        g_warning ("Invalid build type : %s", node->detail);
301
 
                        return -1;
302
 
                }
303
 
        }
304
 
 
305
 
        if (!g_spawn_async_with_pipes (build_dir,
306
 
                                       (char**)argv, NULL,
307
 
                                       0,
308
 
                                       NULL, NULL,
309
 
                                       &pid, 
310
 
                                       NULL, &output, &err,
311
 
                                       NULL)) {
312
 
                g_warning ("Couldn't spawn '%s'", argv[0]);
313
 
                g_free (build_dir);
314
 
                g_free (argv[0]);
315
 
                g_free (argv[1]);
316
 
                return -1;
317
 
        }
318
 
        g_free (build_dir);
319
 
        g_free (argv[0]);
320
 
        g_free (argv[1]);
321
 
 
322
 
        out_channel = g_io_channel_unix_new (output);
323
 
        g_io_channel_set_close_on_unref (out_channel, TRUE);
324
 
        err_channel = g_io_channel_unix_new (err);
325
 
        g_io_channel_set_close_on_unref (err_channel, TRUE);
326
 
 
327
 
        /* Set io channel encoding if current locale is not utf-8. */
328
 
        if (!g_get_charset (&charset)) {
329
 
                if (G_IO_STATUS_NORMAL != g_io_channel_set_encoding (out_channel, charset, &error) ||
330
 
                    G_IO_STATUS_NORMAL != g_io_channel_set_encoding (err_channel, charset, &error)) {
331
 
                        g_io_channel_unref (out_channel);
332
 
                        g_io_channel_unref (err_channel);
333
 
                        g_warning ("Failed to set encodings: %s", error->message);
334
 
                        g_error_free (error);
335
 
                        return -1;
336
 
                }
337
 
        }
338
 
 
339
 
        info = g_new0 (BuildInfo, 1);
340
 
        info->project = project;
341
 
        info->id = ++buildid;
342
 
        info->num_channels = 2;
343
 
        info->callbacks = callbacks;
344
 
        info->build_dir = NULL;
345
 
 
346
 
        /* Intialize regexs. */
347
 
        old_options = re_syntax_options;
348
 
        re_syntax_options = RE_SYNTAX_EGREP;
349
 
 
350
 
        if (!compile_pattern (&info->dir_buf, dir_regex) ||
351
 
            !compile_pattern (&info->warn_buf, warn_regex) ||
352
 
            !compile_pattern (&info->err_buf, err_regex)) {
353
 
                g_io_channel_unref (out_channel);
354
 
                g_io_channel_unref (err_channel);
355
 
                build_info_free (info);
356
 
                g_warning ("failed to compile regexs necessary for build output parsing");
357
 
                return -1;
358
 
        }
359
 
 
360
 
        re_syntax_options = old_options;
361
 
 
362
 
        g_signal_emit_by_name (G_OBJECT (project), "build_start");
363
 
 
364
 
        tmp = g_strjoinv (" ", (char **) argv);
365
 
        msg = g_strconcat (tmp, "\n", NULL);
366
 
        g_free (tmp);
367
 
        build_msg (info, GBF_BUILD_START, msg);
368
 
        g_free (msg);
369
 
 
370
 
        g_io_add_watch (out_channel, G_IO_IN | G_IO_ERR | G_IO_HUP, 
371
 
                        build_output_cb, 
372
 
                        info);
373
 
        g_io_channel_unref (out_channel);
374
 
        g_io_add_watch (err_channel, G_IO_IN | G_IO_ERR | G_IO_HUP, 
375
 
                        build_output_cb, 
376
 
                        info);
377
 
        g_io_channel_unref (err_channel);
378
 
 
379
 
        return info->id;
380
 
}
381
 
 
382
 
#else  /* NATIVE_GNU_REGEX */
383
 
 
384
 
int
385
 
gbf_build_run (GbfMkfileProject    *project,
386
 
               gchar           *id,
387
 
               const char      *project_dir,
388
 
               GList           *callbacks)
389
 
{
390
 
        return -1;
391
 
}
392
 
 
393
 
#endif /* NATIVE_GNU_REGEX */
394
 
 
395
 
void
396
 
gbf_build_cancel (int build_id)
397
 
{
398
 
}