xref: /libCEED/interface/ceed-jit-tools.c (revision 3d8e882215d238700cdceb37404f76ca7fa24eaa)
1 // Copyright (c) 2017-2022, Lawrence Livermore National Security, LLC and other CEED contributors.
2 // All Rights Reserved. See the top-level LICENSE and NOTICE files for details.
3 //
4 // SPDX-License-Identifier: BSD-2-Clause
5 //
6 // This file is part of CEED:  http://github.com/ceed
7 
8 #include <ceed/ceed.h>
9 #include <ceed/backend.h>
10 #include <ceed/jit-tools.h>
11 #include <stdbool.h>
12 #include <stdio.h>
13 #include <string.h>
14 
15 /**
16   @brief Load source file into initalized string buffer, including full text
17            of local files in place of `#include "local.h"`
18 
19   @param ceed                   A Ceed object for error handling
20   @param[in]  source_file_path  Absolute path to source file
21   @param[out] buffer            String buffer for source file contents
22 
23   @return An error code: 0 - success, otherwise - failure
24 
25   @ref Backend
26 **/
27 static inline int CeedLoadSourceToInitalizedBuffer(Ceed ceed,
28     const char *source_file_path, char **buffer) {
29   int ierr;
30   FILE *source_file;
31   long file_size, file_offset = 0;
32   char *temp_buffer;
33 
34   // Debug
35   CeedDebug256(ceed, 1, "---------- Ceed JiT ----------\n");
36   CeedDebug256(ceed, 1, "Current source file: ");
37   CeedDebug256(ceed, 255, "%s\n", source_file_path);
38   CeedDebug256(ceed, 1, "Current buffer:\n");
39   CeedDebug256(ceed, 255, "%s\n", *buffer);
40 
41   // Read file to temporary buffer
42   source_file = fopen(source_file_path, "rb");
43   if (!source_file)
44     // LCOV_EXCL_START
45     return CeedError(ceed, CEED_ERROR_MAJOR, "Couldn't open source file: %s",
46                      source_file_path);
47   // LCOV_EXCL_STOP
48   // -- Compute size of source
49   fseek(source_file, 0L, SEEK_END);
50   file_size = ftell(source_file);
51   rewind(source_file);
52   //  -- Allocate memory for entire source file
53   ierr = CeedCalloc(file_size + 1, &temp_buffer); CeedChk(ierr);
54   // -- Copy the file into the buffer
55   if (1 != fread(temp_buffer, file_size, 1, source_file)) {
56     // LCOV_EXCL_START
57     fclose(source_file);
58     ierr = CeedFree(&temp_buffer); CeedChk(ierr);
59     return CeedError(ceed, CEED_ERROR_MAJOR, "Couldn't read source file: %s",
60                      source_file_path);
61     // LCOV_EXCL_STOP
62   }
63   fclose(source_file);
64 
65   // Search for headers to include
66   const char *first_hash = strchr(temp_buffer, '#');
67   while (first_hash) {
68     // -- Check for 'include' keyword
69     const char *next_e = strchr(first_hash, 'e');
70     char keyword[8] = "";
71     if (next_e)
72       strncpy(keyword, &next_e[-6], 7);
73     bool is_hash_include = !strcmp(keyword, "include");
74     // ---- Spaces allowed in '#  include <header.h>'
75     if (next_e)
76       for (CeedInt i = 1; first_hash - next_e + i < -6; i++)
77         is_hash_include &= first_hash[i] == ' ';
78     if (is_hash_include) {
79       // -- Copy into buffer all preceding #
80       long current_size = strlen(*buffer);
81       long copy_size = first_hash - &temp_buffer[file_offset];
82       ierr = CeedRealloc(current_size + copy_size + 2, buffer); CeedChk(ierr);
83       strncpy(&(*buffer)[current_size], "\n", 2);
84       strncpy(&(*buffer)[current_size + 1], &temp_buffer[file_offset], copy_size);
85       strncpy(&(*buffer)[current_size + copy_size], "", 1);
86       // -- Load local "header.h"
87       char *next_quote = strchr(first_hash, '"');
88       char *next_new_line = strchr(first_hash, '\n');
89       bool is_local_header = is_hash_include && next_quote
90                              && (next_new_line - next_quote > 0);
91       if (is_local_header) {
92         // ---- Build source path
93         char *include_source_path;
94         long root_length = strrchr(source_file_path, '/') - source_file_path;
95         long include_file_name_len = strchr(&next_quote[1], '"') - next_quote - 1;
96         ierr = CeedCalloc(root_length + include_file_name_len + 2,
97                           &include_source_path); CeedChk(ierr);
98         strncpy(include_source_path, source_file_path, root_length + 1);
99         strncpy(&include_source_path[root_length + 1], &next_quote[1],
100                 include_file_name_len);
101         strncpy(&include_source_path[root_length + include_file_name_len + 1], "", 1);
102         // ---- Recursive call to load source to buffer
103         ierr = CeedLoadSourceToInitalizedBuffer(ceed, include_source_path, buffer);
104         CeedChk(ierr);
105         ierr = CeedFree(&include_source_path); CeedChk(ierr);
106       }
107       file_offset = strchr(first_hash, '\n') - temp_buffer + 1;
108     }
109     // -- Next hash
110     first_hash = strchr(&first_hash[1], '#');
111   }
112   // Copy rest of source file into buffer
113   long current_size = strlen(*buffer);
114   long copy_size = strlen(&temp_buffer[file_offset]);
115   ierr = CeedRealloc(current_size + copy_size + 2, buffer); CeedChk(ierr);
116   strncpy(&(*buffer)[current_size], "\n", 2);
117   strncpy(&(*buffer)[current_size + 1], &temp_buffer[file_offset], copy_size);
118   strncpy(&(*buffer)[current_size + copy_size + 1], "", 1);
119 
120   // Cleanup
121   ierr = CeedFree(&temp_buffer); CeedChk(ierr);
122 
123   // Debug
124   CeedDebug256(ceed, 1, "---------- Ceed JiT ----------\n");
125   CeedDebug256(ceed, 1, "Current source file: ");
126   CeedDebug256(ceed, 255, "%s\n", source_file_path);
127   CeedDebug256(ceed, 1, "Final buffer:\n");
128   CeedDebug256(ceed, 255, "%s\n", *buffer);
129 
130   return CEED_ERROR_SUCCESS;
131 }
132 
133 /**
134   @brief Initalize and load source file into string buffer, including full text
135            of local files in place of `#include "local.h"`.
136          Note: Caller is responsible for freeing the string buffer with `CeedFree()`.
137 
138   @param ceed                   A Ceed object for error handling
139   @param[in]  source_file_path  Absolute path to source file
140   @param[out] buffer            String buffer for source file contents
141 
142   @return An error code: 0 - success, otherwise - failure
143 
144   @ref Backend
145 **/
146 int CeedLoadSourceToBuffer(Ceed ceed, const char *source_file_path,
147                            char **buffer) {
148   int ierr;
149 
150   // Initalize buffer
151   ierr = CeedCalloc(1, buffer); CeedChk(ierr);
152 
153   // Load to initalized buffer
154   ierr = CeedLoadSourceToInitalizedBuffer(ceed, source_file_path, buffer);
155   CeedChk(ierr);
156 
157   return CEED_ERROR_SUCCESS;
158 }
159 
160 /**
161   @brief Build an absolute filepath from a base filepath and an absolute filepath.
162            This helps construct source file paths for `CeedLoadSourceToBuffer()`.
163          Note: Caller is responsible for freeing the string buffer with `CeedFree()`.
164 
165   @param ceed                     A Ceed object for error handling
166   @param[in]  base_file_path      Absolute path to current file
167   @param[in]  relative_file_path  Relative path to target file
168   @param[out] new_file_path       String buffer for absolute path to target file
169 
170   @return An error code: 0 - success, otherwise - failure
171 
172   @ref Backend
173 **/
174 int CeedPathConcatenate(Ceed ceed, const char *base_file_path,
175                         const char *relative_file_path, char **new_file_path) {
176   int ierr;
177   char *last_slash = strrchr(base_file_path, '/');
178   size_t base_length = (last_slash - base_file_path + 1),
179          relative_length = strlen(relative_file_path),
180          new_file_path_length = base_length + relative_length + 1;
181 
182   ierr = CeedCalloc(new_file_path_length, new_file_path); CeedChk(ierr);
183   memcpy(*new_file_path, base_file_path, base_length);
184   memcpy(&((*new_file_path)[base_length]), relative_file_path, relative_length);
185 
186   return CEED_ERROR_SUCCESS;
187 }
188