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