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 <ceed-impl.h> 12 #include <stdbool.h> 13 #include <stdio.h> 14 #include <string.h> 15 16 /** 17 @brief Load source file into initalized string buffer, including full text 18 of local files in place of `#include "local.h"` 19 20 @param ceed A Ceed object for error handling 21 @param[in] source_file_path Absolute path to source file 22 @param[out] buffer String buffer for source file contents 23 24 @return An error code: 0 - success, otherwise - failure 25 26 @ref Backend 27 **/ 28 static inline int CeedLoadSourceToInitalizedBuffer(Ceed ceed, 29 const char *source_file_path, char **buffer) { 30 int ierr; 31 FILE *source_file; 32 long file_size, file_offset = 0; 33 char *temp_buffer; 34 35 // Debug 36 CeedDebug256(ceed, 1, "---------- Ceed JiT ----------\n"); 37 CeedDebug256(ceed, 1, "Current source file: "); 38 CeedDebug256(ceed, 255, "%s\n", source_file_path); 39 CeedDebug256(ceed, 1, "Current buffer:\n"); 40 CeedDebug256(ceed, 255, "%s\n", *buffer); 41 42 // Read file to temporary buffer 43 source_file = fopen(source_file_path, "rb"); 44 if (!source_file) 45 // LCOV_EXCL_START 46 return CeedError(ceed, CEED_ERROR_MAJOR, "Couldn't open source file: %s", 47 source_file_path); 48 // LCOV_EXCL_STOP 49 // -- Compute size of source 50 fseek(source_file, 0L, SEEK_END); 51 file_size = ftell(source_file); 52 rewind(source_file); 53 // -- Allocate memory for entire source file 54 ierr = CeedCalloc(file_size + 1, &temp_buffer); CeedChk(ierr); 55 // -- Copy the file into the buffer 56 if (1 != fread(temp_buffer, file_size, 1, source_file)) { 57 // LCOV_EXCL_START 58 fclose(source_file); 59 ierr = CeedFree(&temp_buffer); CeedChk(ierr); 60 return CeedError(ceed, CEED_ERROR_MAJOR, "Couldn't read source file: %s", 61 source_file_path); 62 // LCOV_EXCL_STOP 63 } 64 fclose(source_file); 65 66 // Search for headers to include 67 const char *first_hash = strchr(temp_buffer, '#'); 68 while (first_hash) { 69 // -- Check for 'include' keyword 70 const char *next_e = strchr(first_hash, 'e'); 71 char keyword[8] = ""; 72 if (next_e) 73 strncpy(keyword, &next_e[-6], 7); 74 bool is_hash_include = !strcmp(keyword, "include"); 75 // ---- Spaces allowed in '# include <header.h>' 76 if (next_e) 77 for (CeedInt i = 1; first_hash - next_e + i < -6; i++) 78 is_hash_include &= first_hash[i] == ' '; 79 if (is_hash_include) { 80 // -- Copy into buffer all preceding # 81 long current_size = strlen(*buffer); 82 long copy_size = first_hash - &temp_buffer[file_offset]; 83 ierr = CeedRealloc(current_size + copy_size + 2, buffer); CeedChk(ierr); 84 strncpy(&(*buffer)[current_size], "\n", 2); 85 strncpy(&(*buffer)[current_size + 1], &temp_buffer[file_offset], copy_size); 86 strncpy(&(*buffer)[current_size + copy_size], "", 1); 87 // -- Load local "header.h" 88 char *next_quote = strchr(first_hash, '"'); 89 char *next_new_line = strchr(first_hash, '\n'); 90 bool is_local_header = is_hash_include && next_quote 91 && (next_new_line - next_quote > 0); 92 if (is_local_header) { 93 // ---- Build source path 94 char *include_source_path; 95 long root_length = strrchr(source_file_path, '/') - source_file_path; 96 long include_file_name_len = strchr(&next_quote[1], '"') - next_quote - 1; 97 ierr = CeedCalloc(root_length + include_file_name_len + 2, 98 &include_source_path); CeedChk(ierr); 99 strncpy(include_source_path, source_file_path, root_length + 1); 100 strncpy(&include_source_path[root_length + 1], &next_quote[1], 101 include_file_name_len); 102 strncpy(&include_source_path[root_length + include_file_name_len + 1], "", 1); 103 // ---- Recursive call to load source to buffer 104 ierr = CeedLoadSourceToInitalizedBuffer(ceed, include_source_path, buffer); 105 CeedDebug256(ceed, 2, "JiT Including: %s\n", include_source_path); 106 CeedChk(ierr); 107 ierr = CeedFree(&include_source_path); CeedChk(ierr); 108 } 109 file_offset = strchr(first_hash, '\n') - temp_buffer + 1; 110 } 111 // -- Next hash 112 first_hash = strchr(&first_hash[1], '#'); 113 } 114 // Copy rest of source file into buffer 115 long current_size = strlen(*buffer); 116 long copy_size = strlen(&temp_buffer[file_offset]); 117 ierr = CeedRealloc(current_size + copy_size + 2, buffer); CeedChk(ierr); 118 strncpy(&(*buffer)[current_size], "\n", 2); 119 strncpy(&(*buffer)[current_size + 1], &temp_buffer[file_offset], copy_size); 120 strncpy(&(*buffer)[current_size + copy_size + 1], "", 1); 121 122 // Cleanup 123 ierr = CeedFree(&temp_buffer); CeedChk(ierr); 124 125 // Debug 126 CeedDebug256(ceed, 1, "---------- Ceed JiT ----------\n"); 127 CeedDebug256(ceed, 1, "Current source file: "); 128 CeedDebug256(ceed, 255, "%s\n", source_file_path); 129 CeedDebug256(ceed, 1, "Final buffer:\n"); 130 CeedDebug256(ceed, 255, "%s\n", *buffer); 131 132 return CEED_ERROR_SUCCESS; 133 } 134 135 /** 136 @brief Initalize and load source file into string buffer, including full text 137 of local files in place of `#include "local.h"`. 138 Note: Caller is responsible for freeing the string buffer with `CeedFree()`. 139 140 @param ceed A Ceed object for error handling 141 @param[in] source_file_path Absolute path to source file 142 @param[out] buffer String buffer for source file contents 143 144 @return An error code: 0 - success, otherwise - failure 145 146 @ref Backend 147 **/ 148 int CeedLoadSourceToBuffer(Ceed ceed, const char *source_file_path, 149 char **buffer) { 150 int ierr; 151 152 // Initalize buffer 153 ierr = CeedCalloc(1, buffer); CeedChk(ierr); 154 155 // Load to initalized buffer 156 ierr = CeedLoadSourceToInitalizedBuffer(ceed, source_file_path, buffer); 157 CeedChk(ierr); 158 159 return CEED_ERROR_SUCCESS; 160 } 161 162 /** 163 @brief Build an absolute filepath from a base filepath and an absolute filepath. 164 This helps construct source file paths for `CeedLoadSourceToBuffer()`. 165 Note: Caller is responsible for freeing the string buffer with `CeedFree()`. 166 167 @param ceed A Ceed object for error handling 168 @param[in] base_file_path Absolute path to current file 169 @param[in] relative_file_path Relative path to target file 170 @param[out] new_file_path String buffer for absolute path to target file 171 172 @return An error code: 0 - success, otherwise - failure 173 174 @ref Backend 175 **/ 176 int CeedPathConcatenate(Ceed ceed, const char *base_file_path, 177 const char *relative_file_path, char **new_file_path) { 178 int ierr; 179 char *last_slash = strrchr(base_file_path, '/'); 180 size_t base_length = (last_slash - base_file_path + 1), 181 relative_length = strlen(relative_file_path), 182 new_file_path_length = base_length + relative_length + 1; 183 184 ierr = CeedCalloc(new_file_path_length, new_file_path); CeedChk(ierr); 185 memcpy(*new_file_path, base_file_path, base_length); 186 memcpy(&((*new_file_path)[base_length]), relative_file_path, relative_length); 187 188 return CEED_ERROR_SUCCESS; 189 } 190 191 /** 192 @brief Find the relative filepath to an installed JiT file 193 194 @param[in] absolute_file_path Absolute path to installed JiT file 195 @param[out] relative_file_path Relative path to installed JiT file 196 197 @return An error code: 0 - success, otherwise - failure 198 199 @ref Backend 200 **/ 201 int CeedGetJitRelativePath(const char *absolute_file_path, 202 const char **relative_file_path) { 203 *(relative_file_path) = strstr(absolute_file_path, "ceed/jit-source"); 204 205 if (!*relative_file_path) 206 // LCOV_EXCL_START 207 return CeedError(NULL, CEED_ERROR_MAJOR, 208 "Couldn't find relative path including " 209 "'ceed/jit-source' for: %s", absolute_file_path); 210 // LCOV_EXCL_STOP 211 212 return CEED_ERROR_SUCCESS; 213 } 214 215 /** 216 @brief Build an absolute filepath to a JiT file 217 218 @param ceed A Ceed object for error handling 219 @param[in] relative_file_path Relative path to installed JiT file 220 @param[out] absolute_file_path String buffer for absolute path to target file 221 222 @return An error code: 0 - success, otherwise - failure 223 224 @ref Backend 225 **/ 226 int CeedGetJitAbsolutePath(Ceed ceed, const char *relative_file_path, 227 char **absolute_file_path) { 228 int ierr; 229 230 // Debug 231 CeedDebug256(ceed, 1, "---------- Ceed JiT ----------\n"); 232 CeedDebug256(ceed, 1, "Relative JiT source file: "); 233 CeedDebug256(ceed, 255, "%s\n", relative_file_path); 234 235 236 for (CeedInt i = 0; i < ceed->num_jit_source_roots; i++) { 237 // Debug 238 CeedDebug256(ceed, 1, "Checking JiT root: "); 239 CeedDebug256(ceed, 255, "%s\n", ceed->jit_source_roots[i]); 240 241 // Build absolute path with current root 242 ierr = CeedPathConcatenate(ceed, ceed->jit_source_roots[i], 243 relative_file_path, absolute_file_path); 244 CeedChk(ierr); 245 246 // Temporarily mask function name if included 247 char *last_colon = strrchr(*absolute_file_path, ':'); 248 if (last_colon) { 249 *last_colon = '\0'; 250 } 251 252 // Debug 253 CeedDebug256(ceed, 1, "Checking for source file: "); 254 CeedDebug256(ceed, 255, "%s\n", *absolute_file_path); 255 256 // Check for valid file path 257 FILE *source_file; 258 source_file = fopen((const char *)*absolute_file_path, "rb"); 259 if (source_file) { 260 // Debug 261 CeedDebug256(ceed, 1, "Found JiT source file: "); 262 CeedDebug256(ceed, 255, "%s\n", *absolute_file_path); 263 264 // Restore function name if included 265 if (last_colon) { 266 *last_colon = ':'; 267 } 268 fclose(source_file); 269 return CEED_ERROR_SUCCESS; 270 } else { 271 ierr = CeedFree(absolute_file_path); CeedChk(ierr); 272 } 273 } 274 275 // LCOV_EXCL_START 276 return CeedError(ceed, CEED_ERROR_MAJOR, 277 "Couldn't find matching JiT source file: %s", 278 relative_file_path); 279 // LCOV_EXCL_STOP 280 } 281