]> git.lyx.org Git - lyx.git/blob - lib/examples/Literate.lyx
cleanups from Hartmut
[lyx.git] / lib / examples / Literate.lyx
1 #LyX 1.4.0cvs created this file. For more info see http://www.lyx.org/
2 \lyxformat 243
3 \begin_document
4 \begin_header
5 \textclass literate-article
6 \language english
7 \inputencoding default
8 \fontscheme default
9 \graphics default
10 \paperfontsize default
11 \spacing single
12 \papersize default
13 \use_geometry false
14 \use_amsmath 0
15 \cite_engine basic
16 \use_bibtopic false
17 \paperorientation portrait
18 \secnumdepth 3
19 \tocdepth 3
20 \paragraph_separation indent
21 \defskip medskip
22 \quotes_language english
23 \quotes_times 2
24 \papercolumns 1
25 \papersides 1
26 \paperpagestyle default
27 \tracking_changes false
28 \output_changes true
29 \end_header
30
31 \begin_body
32
33 \begin_layout Title
34
35 LyX and Literate Programming
36 \newline
37 An example program
38 \end_layout
39
40 \begin_layout Author
41
42 Edmar Wienskoski Jr.
43 \newline
44 edmar-w-jr@technologist.com
45 \begin_inset Foot
46 status collapsed
47
48 \begin_layout Standard
49
50 Modified by Bernard Michael Hurley bernardh@westherts.ac.uk ---- Don't blame
51  Edmar for any errors that have crept in!
52 \end_layout
53
54 \end_inset
55
56
57 \end_layout
58
59 \begin_layout Abstract
60
61
62 \series bold
63 Note:
64 \series default
65  This example program is provided for educational use only.
66  The functionality in this C program has been superceded by the equivalent
67  Python code in 
68 \emph on
69 examples/listerrors.lyx
70 \emph default
71  which should be installed in the LyX scripts directory.
72 \end_layout
73
74 \begin_layout Date
75
76
77 \begin_inset ERT
78 status collapsed
79
80 \begin_layout Standard
81
82 \backslash
83 today
84 \end_layout
85
86 \end_inset
87
88
89 \end_layout
90
91 \begin_layout Standard
92
93
94 \begin_inset LatexCommand \tableofcontents{}
95
96 \end_inset
97
98
99 \end_layout
100
101 \begin_layout Section
102
103 Introduction
104 \end_layout
105
106 \begin_layout Standard
107
108 After typesetting a document, LyX scans the LaTeX log file looking for errors.
109  For each error found, the line number is obtained and a error box is displayed
110  in the LyX screen at that position.
111 \end_layout
112
113 \begin_layout Standard
114
115 To use this feature to view compilation errors while working with literate
116  documents, we need a program that filters the compilation errors and puts
117  them in a format suitable for LyX reading it.
118  
119 \end_layout
120
121 \begin_layout Standard
122
123 In this document we present a filter that recognizes compilation error messages
124  from noweb, gnu C, and the IBM C compiler (xlc).
125 \end_layout
126
127 \begin_layout Standard
128
129 The filter is required to read from standard input, parse for error messages
130  and copy the error messages to the standard output.
131  During the output process, the filter must present the error messages in
132  a format that LyX can interpret, currently, the LaTeX error message format.
133  Of course, nothing will prevent future LyX releases from being able to
134  read other formats as well (like gcc error messages for example).
135  This mechanism is necessary to fully explore the literate programming tool's
136  capabilities.
137 \end_layout
138
139 \begin_layout Section
140
141 Algorithm
142 \end_layout
143
144 \begin_layout Scrap
145
146 <<Function bodies>>=
147 \newline
148 int
149 \newline
150 main (int argc, char **argv)
151 \newline
152 {
153 \newline
154   if (argc == 2) {
155 \newline
156     switch (argv[1][0]) {
157 \newline
158     case 'n':
159 \newline
160       <<Scan input for noweb error messages>>
161 \newline
162       break;
163 \newline
164     case 'x':
165 \newline
166       <<Scan input for xlc error messages>>
167 \newline
168       break;
169 \newline
170     case 'a':
171 \newline
172       <<AIX system using both noweb and xlc>>
173 \newline
174       break;
175 \newline
176     case 's':
177 \newline
178     case 'b':
179 \newline
180       <<Solaris and Linux systems using both noweb and gcc>>
181 \newline
182       break;
183 \newline
184     case 'g':
185 \newline
186     default:
187 \newline
188       <<Scan input for gcc error messages>>
189 \newline
190       break;
191 \newline
192     }
193 \newline
194   } else {
195 \newline
196     <<Scan input for gcc error messages>>
197 \newline
198   }
199 \newline
200 }
201 \newline
202 @
203 \end_layout
204
205 \begin_layout Scrap
206
207 <<Function prototypes>>=
208 \newline
209 int main (int argc, char **argv);
210 \newline
211 @
212 \end_layout
213
214 \begin_layout Section
215
216 Data Structures
217 \end_layout
218
219 \begin_layout Standard
220
221 We resort to some global variables to allow access from several different
222  routines.
223  These are the buffer and related pointers used during the parse of the
224  input.
225 \end_layout
226
227 \begin_layout Scrap
228
229 <<Global variables>>=
230 \newline
231 char    buffer[200][200];
232 \newline
233 int     last_buf_line;
234 \newline
235 int     last_err_line;
236 \newline
237 int     err_line;
238 \newline
239
240 \end_layout
241
242 \begin_layout Section
243
244 The output format
245 \end_layout
246
247 \begin_layout Standard
248
249 The output format mimics the TeX error messages format.
250  This function prints a number of lines residing in the global variable
251  
252 \family typewriter
253 buffer
254 \family default
255 , a program name and line number.
256  There is no special requirement on the input strings, they can be anything.
257 \begin_inset Foot
258 status collapsed
259
260 \begin_layout Standard
261
262 This function has been slightly changed from EW's original to make scanning
263  a bit easier with LaTeX::scanLogFile().
264  The test has been added because LyX can crash if empty lines are allowed
265  here --- I can't figure out why! --- BMH
266 \end_layout
267
268 \end_inset
269
270
271 \end_layout
272
273 \begin_layout Scrap
274
275 <<Function bodies>>=
276 \newline
277 void
278 \newline
279 output_error (int buf_size, int error_line, char *tool)
280 \newline
281 {
282 \newline
283   int     i;
284 \newline
285  
286 \newline
287   fprintf(stdout, "! Build Error: ==> %s ==>
288 \backslash
289 n", tool);
290 \newline
291   fprintf(stdout, " ...
292 \backslash
293 n
294 \backslash
295 nl.%d ...
296 \backslash
297 n", error_line);
298 \newline
299  
300 \newline
301   for (i=0; i<buf_size; i++)
302 \newline
303     if (strlen(buffer[i]) != 0)
304 \newline
305       fprintf(stdout, "%s", buffer[i]);
306 \newline
307  
308 \newline
309   fprintf(stdout, "
310 \backslash
311 n");
312 \newline
313 }
314 \newline
315 @
316 \end_layout
317
318 \begin_layout Scrap
319
320 <<Function prototypes>>=
321 \newline
322 void output_error (int buf_size, int error_line, char *tool);
323 \newline
324 @
325 \end_layout
326
327 \begin_layout Section
328
329 Functions Implementation
330 \end_layout
331
332 \begin_layout Standard
333
334 Both noweave and notangle routines, always output one single line for each
335  error found, thus to scan the buffer for noweb error messages is enough
336  to exam one input line at a time.
337  Note that the noweb software does not provide a line error number, so all
338  errors boxes related to noweb messages will be displayed at the beginning
339  of the file.
340 \end_layout
341
342 \begin_layout Scrap
343
344 <<Scan input for noweb error messages>>=
345 \newline
346 {
347 \newline
348   last_buf_line = 0;
349 \newline
350   while (fgets(buffer[0], 200, stdin)) {
351 \newline
352     if (noweb_try(0))
353 \newline
354       output_error(1, err_line, "noweb");
355 \newline
356   }
357 \newline
358 }
359 \newline
360 @
361 \end_layout
362
363 \begin_layout Standard
364
365 The examination itself is very inefficient.
366  Unfortunately noweb doesn't have any characteristic that would help to
367  identify one of its error messages.
368  The solution is to collect all possible output messages in an array of
369  strings, and turn the examination process into a linear search in this
370  array.
371 \end_layout
372
373 \begin_layout Scrap
374
375 <<Global variables>>=
376 \newline
377 char *noweb_msgs[] = {
378 \newline
379   "couldn't open file",
380 \newline
381   "couldn't open temporary file",
382 \newline
383   "error writing temporary file",
384 \newline
385   "ill-formed option",
386 \newline
387   "unknown option",
388 \newline
389   "Bad format sequence",
390 \newline
391   "Can't open output file",
392 \newline
393   "Can't open temporary file",
394 \newline
395   "Capacity exceeded:",
396 \newline
397   "Ignoring unknown option -",
398 \newline
399   "This can't happen:",
400 \newline
401   "non-numeric line number in"
402 \newline
403 };
404 \newline
405
406 \newline
407 char *noweb_msgs_mimic_gcc[] = {
408 \newline
409   ": unescaped << in documentation chunk"
410 \newline
411 };
412 \newline
413 @
414 \end_layout
415
416 \begin_layout Standard
417
418 A noweb error message can be any string that contains a matching pair of
419  < <\InsetSpace ~
420 \InsetSpace ~
421 \InsetSpace ~
422 > >, or any of the above strings
423 \end_layout
424
425 \begin_layout Scrap
426
427 <<Function bodies>>=
428 \newline
429 int
430 \newline
431 noweb_try (int buf_line)
432 \newline
433 {
434 \newline
435   char    *s, *t, *b;
436 \newline
437   int     i; 
438 \newline
439
440 \newline
441   b = buffer[buf_line];
442 \newline
443   err_line = 0;
444 \newline
445
446 \newline
447   for (i=0; i<1; i++) {
448 \newline
449       s = (char *)strstr (b, noweb_msgs_mimic_gcc[i]);
450 \newline
451       if (s != NULL) {
452 \newline
453         t = (char *)strchr(buffer[buf_line], ':');
454 \newline
455         err_line = atoi(t+1);
456 \newline
457         t = buffer[buf_line];
458 \newline
459         ++s;
460 \newline
461         while (*(t++) = *(s++));
462 \newline
463         return 1;
464 \newline
465       }
466 \newline
467   }
468 \newline
469   s = (char *)strstr(b, "<<");
470 \newline
471   if (s != NULL) {
472 \newline
473     s = (char *)strstr(s+2, ">>");
474 \newline
475     if (s != NULL) {
476 \newline
477       return 1;
478 \newline
479     }
480 \newline
481   } else { 
482 \newline
483      for (i = 0; i < 12; ++i) {
484 \newline
485         s = (char *)strstr (b, noweb_msgs[i]);
486 \newline
487         if (s != NULL) {
488 \newline
489            return 1;
490 \newline
491         }
492 \newline
493     }
494 \newline
495   }
496 \newline
497   return 0;
498 \newline
499 }
500 \newline
501 @
502 \end_layout
503
504 \begin_layout Scrap
505
506 <<Function prototypes>>=
507 \newline
508 int noweb_try (int buf_line);
509 \newline
510 @
511 \end_layout
512
513 \begin_layout Standard
514
515 The xlc compiler always outputs one single line for each error found, thus
516  to scan the buffer for xlc error messages it is enough to exam one input
517  line at a time.
518 \end_layout
519
520 \begin_layout Scrap
521
522 <<Scan input for xlc error messages>>= 
523 \newline
524 {
525 \newline
526   last_buf_line = 0;
527 \newline
528   while (fgets(buffer[last_buf_line], 200, stdin)) {
529 \newline
530     if (xlc_try(0))
531 \newline
532       output_error(1, err_line, "xlc");
533 \newline
534   }
535 \newline
536 }
537 \newline
538 @
539 \end_layout
540
541 \begin_layout Standard
542
543 A xlc error message is easy to identify.
544  Every error message starts with a quoted string with no spaces, a comma,
545  a space, the word 
546 \begin_inset Quotes eld
547 \end_inset
548
549 line
550 \begin_inset Quotes erd
551 \end_inset
552
553 , a space, and some variable text.
554  The following routine tests if a given buffer line matches this criteria:
555 \end_layout
556
557 \begin_layout Scrap
558
559 <<Function bodies>>=
560 \newline
561 int 
562 \newline
563 xlc_try (int buf_line)
564 \newline
565 {
566 \newline
567   char    *s, *t;
568 \newline
569  
570 \newline
571   t = buffer[buf_line];
572 \newline
573   s = t+1;
574 \newline
575   while (*s != '"' && *s != ' ' && *s != '
576 \backslash
577 0')
578 \newline
579     s++;
580 \newline
581   if (*t != '"' || *s != '"' || strncmp(s+1, ", line ", 7) != 0)
582 \newline
583     return 0;
584 \newline
585   s += 8;
586 \newline
587   err_line = atoi(s);
588 \newline
589   return 1;
590 \newline
591 }
592 \newline
593 @
594 \end_layout
595
596 \begin_layout Scrap
597
598 <<Function prototypes>>=
599 \newline
600 int xlc_try (int buf_line);
601 \newline
602 @
603 \end_layout
604
605 \begin_layout Standard
606
607 The gcc compiler error messages are more complicated to scan.
608  Each error can span more than one line in the buffer.
609  The good news is that every buffer line on each error has the same pattern,
610  and share the same line number.
611  Thus the strategy will be to accumulate lines in the buffer while the reported
612  line number is still the same.
613  At the time they differ, all the accumulated lines, except the last one,
614  will belong to one single error message, which now can be output-ed to
615  LyX.
616 \end_layout
617
618 \begin_layout Standard
619
620 Every gcc error message contains a string with no space followed by a 
621 \begin_inset Quotes eld
622 \end_inset
623
624 :
625 \begin_inset Quotes eld
626 \end_inset
627
628 .
629  If the next character is a space, then this line is a header of a error
630  message and the next line will detail the line number of the source code
631  where the error was found.
632  Otherwise, the next thing is a integer number followed by another 
633 \begin_inset Quotes eld
634 \end_inset
635
636 :
637 \begin_inset Quotes eld
638 \end_inset
639
640 .
641 \end_layout
642
643 \begin_layout Scrap
644
645 <<Scan input for gcc error messages>>=
646 \newline
647 {
648 \newline
649   char    *s, *t;
650 \newline
651  
652 \newline
653   last_buf_line = 0;
654 \newline
655   while (fgets(buffer[last_buf_line], 200, stdin)) {
656 \newline
657     /****** Skip lines until I find an error */
658 \newline
659     s = (char *)strpbrk(buffer[last_buf_line], " :");
660 \newline
661     if (s == NULL || *s == ' ')
662 \newline
663       continue; /* No gcc error found here */
664 \newline
665     do {
666 \newline
667       <<gcc error message criteria is to find a "...:999:" or a "...: ">>
668 \newline
669       /****** OK It is an error message, get line number */
670 \newline
671       err_line = atoi(s+1);
672 \newline
673       if (last_err_line == 0 || last_err_line == err_line) {
674 \newline
675         last_err_line = err_line;
676 \newline
677         continue; /* It's either a header or a continuation, don't output
678  yet */
679 \newline
680       }
681 \newline
682       /****** Completed the scan of one error message, output it to LyX
683  */
684 \newline
685       discharge_buffer(1);
686 \newline
687       break;
688 \newline
689     } while (fgets(buffer[last_buf_line], 200, stdin));
690 \newline
691   }
692 \newline
693   /****** EOF completes the scan of whatever was being scanned */
694 \newline
695   discharge_buffer(0);
696 \newline
697 }
698 \newline
699 @
700 \end_layout
701
702 \begin_layout Scrap
703
704 <<gcc error message criteria is to find a "...:999:" or a "...: ">>=
705 \newline
706 /****** Search first ":" in the error number */
707 \newline
708 s = (char *)strpbrk(buffer[last_buf_line], " :");
709 \newline
710 last_buf_line++;
711 \newline
712 if (s == NULL || *s == ' ') 
713 \newline
714   <<No gcc error found here, but it might terminate the scanning of a previous
715  one>>
716 \newline
717 /****** Search second ":" in the error number */
718 \newline
719 t = (char *)strpbrk(s+1, " :");
720 \newline
721 if (t == NULL || *t == ' ')
722 \newline
723   <<No gcc error found here, but it might terminate the scanning of a previous
724  one>>
725 \newline
726 /****** Verify if is all digits between ":" */
727 \newline
728 if (t != s+1+strspn(s+1, "0123456789")) 
729 \newline
730   <<No gcc error found here, but it might terminate the scanning of a previous
731  one>>
732 \newline
733 @
734 \end_layout
735
736 \begin_layout Scrap
737
738 <<No gcc error found here, but it might terminate the scanning of a previous
739  one>>=
740 \newline
741 {
742 \newline
743   err_line = 0;
744 \newline
745   discharge_buffer(1);
746 \newline
747   continue;
748 \newline
749 }
750 \newline
751 @
752 \end_layout
753
754 \begin_layout Standard
755
756 As we mentioned, when the scan of one gcc error message is completed everything
757  in the buffer except the last line is one single error message.
758  But if the scan terminates with a EOF or through finding one line that
759  does not match the gcc error message criteria, then there is no 
760 \begin_inset Quotes eld
761 \end_inset
762
763 last line
764 \begin_inset Quotes erd
765 \end_inset
766
767  in the buffer to be concerned with.
768  In those cases we empty the buffer completely.
769 \end_layout
770
771 \begin_layout Scrap
772
773 <<Function bodies>>=
774 \newline
775 void
776 \newline
777 discharge_buffer (int save_last)
778 \newline
779 {
780 \newline
781  if (last_err_line != 0) { 
782 \newline
783    clean_gcc_messages();
784 \newline
785    if (save_last != 0) {
786 \newline
787       output_error(last_buf_line-1, last_err_line, "gcc");
788 \newline
789       strcpy (buffer[0], buffer[last_buf_line-1]);
790 \newline
791       last_err_line = err_line;
792 \newline
793       last_buf_line = 1;
794 \newline
795     } else { 
796 \newline
797       ++last_buf_line;
798 \newline
799       clean_gcc_messages();
800 \newline
801       output_error(last_buf_line-1, last_err_line, "gcc");
802 \newline
803       last_err_line = 0;
804 \newline
805       last_buf_line = 0;
806 \newline
807     }
808 \newline
809   }
810 \newline
811 }
812 \newline
813 @
814 \end_layout
815
816 \begin_layout Scrap
817
818 <<Function prototypes>>=
819 \newline
820 void discharge_buffer (int save_last);
821 \newline
822 @
823 \end_layout
824
825 \begin_layout Standard
826
827 The next function 
828 \begin_inset Quotes eld
829 \end_inset
830
831 cleans
832 \begin_inset Quotes erd
833 \end_inset
834
835  superfluous information from gcc messages, namely the name of the noweb
836  file and the line number of the Error.
837 \begin_inset Foot
838 status collapsed
839
840 \begin_layout Standard
841
842 More could be done.
843  For instance, some way of distinguishing between gcc Errors and Warnings
844  should be devised.
845 \end_layout
846
847 \end_inset
848
849
850 \end_layout
851
852 \begin_layout Scrap
853
854 <<Function bodies>>=
855 \newline
856 void
857 \newline
858 clean_gcc_messages ()
859 \newline
860 {
861 \newline
862   int index;
863 \newline
864   char search [30]; 
865 \newline
866   char *tail, *head; 
867 \newline
868   int search_len = sprintf(search, ".nw:%d:", last_err_line);
869 \newline
870   
871 \newline
872   for (index = 0; index < last_buf_line-1; index++) {
873 \newline
874     tail = (char *)strstr (buffer[index], search);
875 \newline
876     if ( tail == NULL) {
877 \newline
878        tail = (char *) strstr (buffer[index], ".nw:");
879 \newline
880        if (tail) {
881 \newline
882           tail += 4;
883 \newline
884        }
885 \newline
886     } else {
887 \newline
888        tail += search_len;
889 \newline
890     }
891 \newline
892     if (tail != NULL) {
893 \newline
894        head = buffer[index];
895 \newline
896        while (*(head++) = *(tail++));
897 \newline
898     }
899 \newline
900   }
901 \newline
902 }
903 \newline
904 @
905 \end_layout
906
907 \begin_layout Scrap
908
909 <<Function prototypes>>=
910 \newline
911 void clean_gcc_messages ();
912 \newline
913 @
914 \end_layout
915
916 \begin_layout Standard
917
918 To combine the scan of noweb error messages and xlc error messages is very
919  simple.
920  We just try each one for every input line:
921 \end_layout
922
923 \begin_layout Scrap
924
925 <<AIX system using both noweb and xlc>>=
926 \newline
927 {
928 \newline
929   last_buf_line = 0;
930 \newline
931   while (fgets(buffer[0], 200, stdin)) {
932 \newline
933     if (noweb_try(0))
934 \newline
935       output_error(1, err_line, "noweb");
936 \newline
937     else if (xlc_try(0))
938 \newline
939       output_error(1, err_line, "xlc");
940 \newline
941   }
942 \newline
943 }
944 \newline
945 @
946 \end_layout
947
948 \begin_layout Standard
949
950 To combine the scan of noweb error messages and gcc error messages is simple
951  if we realize that it is not possible to find a noweb error message in
952  the middle of a gcc error message.
953  So we just repeat the gcc procedure and test for noweb error messages in
954  the beginning of the scan:
955 \end_layout
956
957 \begin_layout Scrap
958
959 <<Solaris and Linux systems using both noweb and gcc>>=
960 \newline
961 {
962 \newline
963   char    *s, *t;
964 \newline
965  
966 \newline
967   last_buf_line = 0;
968 \newline
969   while (fgets(buffer[last_buf_line], 200, stdin)) {
970 \newline
971     /****** Skip lines until I find an error */
972 \newline
973     if (last_buf_line == 0 && noweb_try(0)) {
974 \newline
975       output_error(1, err_line, "noweb");
976 \newline
977       continue;
978 \newline
979     }
980 \newline
981     s = (char *)strpbrk(buffer[last_buf_line], " :");
982 \newline
983     if (s == NULL || *s == ' ')
984 \newline
985       continue; /* No gcc error found here */
986 \newline
987     do {
988 \newline
989       <<gcc error message criteria is to find a "...:999:" or a "...: ">>
990 \newline
991       /****** OK It is an error, get line number */
992 \newline
993       err_line = atoi(s+1);
994 \newline
995       if (last_err_line == 0 || last_err_line == err_line) {
996 \newline
997         last_err_line = err_line;
998 \newline
999         continue; /* It's either a header or a continuation, don't output
1000  yet */
1001 \newline
1002       }
1003 \newline
1004       /****** Completed the scan of one error message, output it to LyX
1005  */
1006 \newline
1007       discharge_buffer(1);
1008 \newline
1009       break;
1010 \newline
1011     } while (fgets(buffer[last_buf_line], 200, stdin));
1012 \newline
1013   }
1014 \newline
1015   /****** EOF completes the scan of whatever was being scanned */
1016 \newline
1017   discharge_buffer(0);
1018 \newline
1019 }
1020 \newline
1021 @
1022 \end_layout
1023
1024 \begin_layout Section
1025
1026 Wrapping the code into a file
1027 \end_layout
1028
1029 \begin_layout Scrap
1030
1031 <<listerrors.c>>=
1032 \newline
1033 #include <stdio.h>
1034 \newline
1035 #include <strings.h>       
1036 \newline
1037  
1038 \newline
1039 <<Global variables>>
1040 \newline
1041 <<Function prototypes>>
1042 \newline
1043 <<Function bodies>>
1044 \newline
1045 @
1046 \end_layout
1047
1048 \begin_layout Standard
1049
1050 To build this program, we want to add the 
1051 \begin_inset Quotes eld
1052 \end_inset
1053
1054 -L
1055 \begin_inset Quotes erd
1056 \end_inset
1057
1058  option in the tangle command to force gdb to load the file 
1059 \family typewriter
1060 Literate.nw
1061 \family default
1062  instead of 
1063 \family typewriter
1064 listerrors.c
1065 \family default
1066 .
1067  In accordance with this, we pass the 
1068 \begin_inset Quotes eld
1069 \end_inset
1070
1071 -g
1072 \begin_inset Quotes erd
1073 \end_inset
1074
1075  option to gcc.
1076 \end_layout
1077
1078 \begin_layout Scrap
1079
1080 <<build-script>>=
1081 \newline
1082 #!/bin/sh
1083 \newline
1084 if [ -z "$NOWEB_SOURCE" ]; then NOWEB_SOURCE=Literate.nw; fi
1085 \newline
1086 notangle -L -Rlisterrors.c ${NOWEB_SOURCE} > listerrors.c
1087 \newline
1088 gcc -g -o listerrors listerrors.c
1089 \newline
1090 @
1091 \end_layout
1092
1093 \begin_layout Standard
1094
1095 This project can be tangled and compiled from LyX if you set 
1096 \family typewriter
1097
1098 \backslash
1099 build_command
1100 \family default
1101  to call a generic script that always extracts a scrap named 
1102 \family typewriter
1103 build-script
1104 \family default
1105  and executes it.
1106  Here is a example of such generic script:
1107 \end_layout
1108
1109 \begin_layout LyX-Code
1110
1111 #!/bin/sh
1112 \newline
1113 notangle -Rbuild-script $1 | env NOWEB_SOURCE=$1 sh
1114 \end_layout
1115
1116 \begin_layout LyX-Code
1117
1118 \end_layout
1119
1120 \end_body
1121 \end_document