1 #define PETSC_DESIRE_FEATURE_TEST_MACROS /* for strdup() */ 2 #include <petsc/private/petscimpl.h> /*I "petscsys.h" I*/ 3 4 #if defined(PETSC_HAVE_YAML) 5 #include <yaml.h> /* use external LibYAML */ 6 #else 7 #include <../src/sys/yaml/include/yaml.h> 8 #endif 9 10 PETSC_INTERN PetscErrorCode PetscOptionsSetValue_Private(PetscOptions, const char[], const char[], int *, PetscOptionSource); 11 PETSC_INTERN PetscErrorCode PetscOptionsInsertStringYAML_Private(PetscOptions, const char[], PetscOptionSource); 12 13 static MPI_Comm petsc_yaml_comm = MPI_COMM_NULL; /* only used for parallel error handling */ 14 15 static inline MPI_Comm PetscYAMLGetComm(void) 16 { 17 return PetscLikely(petsc_yaml_comm != MPI_COMM_NULL) ? petsc_yaml_comm : (petsc_yaml_comm = PETSC_COMM_SELF); 18 } 19 20 static inline MPI_Comm PetscYAMLSetComm(MPI_Comm comm) 21 { 22 MPI_Comm prev = PetscYAMLGetComm(); 23 petsc_yaml_comm = comm; 24 return prev; 25 } 26 27 #define TAG(node) ((const char *)((node)->tag)) 28 #define STR(node) ((const char *)((node)->data.scalar.value)) 29 #define SEQ(node) ((node)->data.sequence.items) 30 #define MAP(node) ((node)->data.mapping.pairs) 31 32 static PetscErrorCode PetscParseLayerYAML(PetscOptions options, yaml_document_t *doc, yaml_node_t *node, PetscOptionSource source) 33 { 34 MPI_Comm comm = PetscYAMLGetComm(); 35 char name[PETSC_MAX_OPTION_NAME] = "", prefix[PETSC_MAX_OPTION_NAME] = ""; 36 37 PetscFunctionBegin; 38 if (node->type == YAML_SCALAR_NODE && !STR(node)[0]) PetscFunctionReturn(PETSC_SUCCESS); /* empty */ 39 PetscCheck(node->type == YAML_MAPPING_NODE, comm, PETSC_ERR_SUP, "Unsupported YAML node type: expected mapping"); 40 for (yaml_node_pair_t *pair = MAP(node).start; pair < MAP(node).top; pair++) { 41 yaml_node_t *keynode = yaml_document_get_node(doc, pair->key); 42 yaml_node_t *valnode = yaml_document_get_node(doc, pair->value); 43 PetscBool isMergeKey, isDummyKey, isIncludeTag; 44 45 PetscCheck(keynode, comm, PETSC_ERR_LIB, "Corrupt YAML document"); 46 PetscCheck(valnode, comm, PETSC_ERR_LIB, "Corrupt YAML document"); 47 PetscCheck(keynode->type == YAML_SCALAR_NODE, comm, PETSC_ERR_SUP, "Unsupported YAML node type: expected scalar"); 48 49 /* "<<" is the merge key: don't increment the prefix */ 50 PetscCall(PetscStrcmp(STR(keynode), "<<", &isMergeKey)); 51 if (isMergeKey) { 52 if (valnode->type == YAML_SEQUENCE_NODE) { 53 for (yaml_node_item_t *item = SEQ(valnode).start; item < SEQ(valnode).top; item++) { 54 yaml_node_t *itemnode = yaml_document_get_node(doc, *item); 55 PetscCheck(itemnode, comm, PETSC_ERR_LIB, "Corrupt YAML document"); 56 PetscCheck(itemnode->type == YAML_MAPPING_NODE, comm, PETSC_ERR_SUP, "Unsupported YAML node type: expected mapping"); 57 PetscCall(PetscParseLayerYAML(options, doc, itemnode, source)); 58 } 59 } else if (valnode->type == YAML_MAPPING_NODE) { 60 PetscCall(PetscParseLayerYAML(options, doc, valnode, source)); 61 } else SETERRQ(comm, PETSC_ERR_SUP, "Unsupported YAML node type: expected sequence or mapping"); 62 continue; /* to next pair */ 63 } 64 65 /* "$$*" are treated as dummy keys, we use them for !include tags and to define anchors */ 66 PetscCall(PetscStrbeginswith(STR(keynode), "$$", &isDummyKey)); 67 if (isDummyKey) { 68 PetscCall(PetscStrendswith(TAG(valnode), "!include", &isIncludeTag)); 69 if (isIncludeTag) { /* TODO: add proper support relative paths */ 70 PetscCall(PetscOptionsInsertFileYAML(comm, options, STR(valnode), PETSC_TRUE)); 71 } 72 continue; /* to next pair */ 73 } 74 75 if (valnode->type == YAML_SCALAR_NODE) { 76 PetscCall(PetscSNPrintf(name, sizeof(name), "-%s", STR(keynode))); 77 PetscCall(PetscOptionsSetValue_Private(options, name, STR(valnode), NULL, source)); 78 79 } else if (valnode->type == YAML_SEQUENCE_NODE) { 80 PetscSegBuffer seg; 81 char *buf, *strlist; 82 PetscBool addSep = PETSC_FALSE; 83 84 PetscCall(PetscSegBufferCreate(sizeof(char), PETSC_MAX_PATH_LEN, &seg)); 85 for (yaml_node_item_t *item = SEQ(valnode).start; item < SEQ(valnode).top; item++) { 86 yaml_node_t *itemnode = yaml_document_get_node(doc, *item); 87 const char *itemstr = NULL; 88 size_t itemlen; 89 90 PetscCheck(itemnode, comm, PETSC_ERR_LIB, "Corrupt YAML document"); 91 92 if (itemnode->type == YAML_SCALAR_NODE) { 93 itemstr = STR(itemnode); 94 95 } else if (itemnode->type == YAML_MAPPING_NODE) { 96 yaml_node_pair_t *kvn = itemnode->data.mapping.pairs.start; 97 yaml_node_pair_t *top = itemnode->data.mapping.pairs.top; 98 99 PetscCheck(top - kvn <= 1, comm, PETSC_ERR_SUP, "Unsupported YAML node value: expected a single key:value pair"); 100 if (top - kvn > 0) { 101 yaml_node_t *kn = yaml_document_get_node(doc, kvn->key); 102 yaml_node_t *vn = yaml_document_get_node(doc, kvn->value); 103 104 PetscCheck(kn, comm, PETSC_ERR_LIB, "Corrupt YAML document"); 105 PetscCheck(vn, comm, PETSC_ERR_LIB, "Corrupt YAML document"); 106 PetscCheck(kn->type == YAML_SCALAR_NODE, comm, PETSC_ERR_SUP, "Unsupported YAML node type: expected scalar"); 107 108 PetscCall(PetscStrcmp(STR(kn), "<<", &isMergeKey)); 109 PetscCheck(!isMergeKey, comm, PETSC_ERR_SUP, "Unsupported YAML node value: merge key '<<' not supported here"); 110 111 PetscCall(PetscStrbeginswith(STR(kn), "$$", &isDummyKey)); 112 if (isDummyKey) continue; 113 itemstr = STR(kn); 114 } 115 116 PetscCall(PetscSNPrintf(prefix, sizeof(prefix), "%s_", STR(keynode))); 117 PetscCall(PetscOptionsPrefixPush(options, prefix)); 118 PetscCall(PetscParseLayerYAML(options, doc, itemnode, source)); 119 PetscCall(PetscOptionsPrefixPop(options)); 120 121 } else SETERRQ(comm, PETSC_ERR_SUP, "Unsupported YAML node type: expected scalar or mapping"); 122 123 PetscCall(PetscStrlen(itemstr, &itemlen)); 124 if (itemlen) { 125 if (addSep) { 126 PetscCall(PetscSegBufferGet(seg, 1, &buf)); 127 PetscCall(PetscArraycpy(buf, ",", 1)); 128 } 129 PetscCall(PetscSegBufferGet(seg, itemlen, &buf)); 130 PetscCall(PetscArraycpy(buf, itemstr, itemlen)); 131 addSep = PETSC_TRUE; 132 } 133 } 134 PetscCall(PetscSegBufferGet(seg, 1, &buf)); 135 PetscCall(PetscArrayzero(buf, 1)); 136 PetscCall(PetscSegBufferExtractAlloc(seg, &strlist)); 137 PetscCall(PetscSegBufferDestroy(&seg)); 138 139 PetscCall(PetscSNPrintf(name, sizeof(name), "-%s", STR(keynode))); 140 PetscCall(PetscOptionsSetValue_Private(options, name, strlist, NULL, source)); 141 PetscCall(PetscFree(strlist)); 142 143 } else if (valnode->type == YAML_MAPPING_NODE) { 144 PetscCall(PetscSNPrintf(prefix, sizeof(prefix), "%s_", STR(keynode))); 145 PetscCall(PetscOptionsPrefixPush(options, prefix)); 146 PetscCall(PetscParseLayerYAML(options, doc, valnode, source)); 147 PetscCall(PetscOptionsPrefixPop(options)); 148 149 } else SETERRQ(comm, PETSC_ERR_SUP, "Unsupported YAML node type: expected scalar, sequence or mapping"); 150 } 151 PetscFunctionReturn(PETSC_SUCCESS); 152 } 153 154 PetscErrorCode PetscOptionsInsertStringYAML_Private(PetscOptions options, const char in_str[], PetscOptionSource source) 155 { 156 MPI_Comm comm = PetscYAMLGetComm(); 157 yaml_parser_t parser; 158 yaml_document_t doc; 159 yaml_node_t *root; 160 int err; 161 162 PetscFunctionBegin; 163 if (!in_str) in_str = ""; 164 err = !yaml_parser_initialize(&parser); 165 PetscCheck(!err, comm, PETSC_ERR_LIB, "YAML parser initialization error"); 166 yaml_parser_set_input_string(&parser, (const unsigned char *)in_str, strlen(in_str)); 167 do { 168 err = !yaml_parser_load(&parser, &doc); 169 PetscCheck(!err, comm, PETSC_ERR_LIB, "YAML parser loading error"); 170 root = yaml_document_get_root_node(&doc); 171 if (root) PetscCall(PetscParseLayerYAML(options, &doc, root, source)); 172 yaml_document_delete(&doc); 173 } while (root); 174 yaml_parser_delete(&parser); 175 PetscFunctionReturn(PETSC_SUCCESS); 176 } 177 178 /*@ 179 PetscOptionsInsertStringYAML - Inserts YAML-formatted options into the options database from a string 180 181 Logically Collective 182 183 Input Parameters: 184 + options - options database, use `NULL` for default global database 185 - in_str - YAML-formatted string options 186 187 Level: intermediate 188 189 .seealso: `PetscOptionsSetValue()`, `PetscOptionsView()`, `PetscOptionsHasName()`, `PetscOptionsGetInt()`, 190 `PetscOptionsGetReal()`, `PetscOptionsGetString()`, `PetscOptionsGetIntArray()`, `PetscOptionsBool()`, 191 `PetscOptionsName()`, `PetscOptionsBegin()`, `PetscOptionsEnd()`, `PetscOptionsHeadBegin()`, 192 `PetscOptionsStringArray()`, `PetscOptionsRealArray()`, `PetscOptionsScalar()`, 193 `PetscOptionsBoolGroupBegin()`, `PetscOptionsBoolGroup()`, `PetscOptionsBoolGroupEnd()`, 194 `PetscOptionsFList()`, `PetscOptionsEList()`, `PetscOptionsInsertFile()`, `PetscOptionsInsertFileYAML()` 195 @*/ 196 PetscErrorCode PetscOptionsInsertStringYAML(PetscOptions options, const char in_str[]) 197 { 198 PetscFunctionBegin; 199 PetscCall(PetscOptionsInsertStringYAML_Private(options, in_str, PETSC_OPT_CODE)); 200 PetscFunctionReturn(PETSC_SUCCESS); 201 } 202 203 /*@ 204 PetscOptionsInsertFileYAML - Insert a YAML-formatted file in the options database 205 206 Collective 207 208 Input Parameters: 209 + comm - the processes that will share the options (usually `PETSC_COMM_WORLD`) 210 . options - options database, use `NULL` for default global database 211 . file - name of file 212 - require - if `PETSC_TRUE` will generate an error if the file does not exist 213 214 Level: intermediate 215 216 Notes: 217 PETSc will generate an error condition that stops the program if a YAML error 218 is detected, hence the user should check that the YAML file is valid before 219 supplying it, for instance at <http://www.yamllint.com> . 220 221 Uses `PetscOptionsInsertStringYAML()`. 222 223 .seealso: `PetscOptionsSetValue()`, `PetscOptionsView()`, `PetscOptionsHasName()`, `PetscOptionsGetInt()`, 224 `PetscOptionsGetReal()`, `PetscOptionsGetString()`, `PetscOptionsGetIntArray()`, `PetscOptionsBool()`, 225 `PetscOptionsName()`, `PetscOptionsBegin()`, `PetscOptionsEnd()`, `PetscOptionsHeadBegin()`, 226 `PetscOptionsStringArray()`, `PetscOptionsRealArray()`, `PetscOptionsScalar()`, 227 `PetscOptionsBoolGroupBegin()`, `PetscOptionsBoolGroup()`, `PetscOptionsBoolGroupEnd()`, 228 `PetscOptionsFList()`, `PetscOptionsEList()`, `PetscOptionsInsertFile()`, `PetscOptionsInsertStringYAML()` 229 @*/ 230 PetscErrorCode PetscOptionsInsertFileYAML(MPI_Comm comm, PetscOptions options, const char file[], PetscBool require) 231 { 232 int yamlLength = -1; 233 char *yamlString = NULL; 234 MPI_Comm prev; 235 PetscMPIInt rank; 236 237 PetscFunctionBegin; 238 PetscCallMPI(MPI_Comm_rank(comm, &rank)); 239 if (rank == 0) { 240 char fpath[PETSC_MAX_PATH_LEN]; 241 char fname[PETSC_MAX_PATH_LEN]; 242 FILE *fd; 243 size_t rd; 244 245 PetscCall(PetscStrreplace(PETSC_COMM_SELF, file, fpath, sizeof(fpath))); 246 PetscCall(PetscFixFilename(fpath, fname)); 247 248 fd = fopen(fname, "r"); 249 if (fd) { 250 fseek(fd, 0, SEEK_END); 251 yamlLength = (int)ftell(fd); 252 fseek(fd, 0, SEEK_SET); 253 PetscCheck(yamlLength >= 0, PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN, "Unable to query size of YAML file: %s", fname); 254 PetscCall(PetscMalloc1(yamlLength + 1, &yamlString)); 255 rd = fread(yamlString, 1, (size_t)yamlLength, fd); 256 PetscCheck(rd == (size_t)yamlLength, PETSC_COMM_SELF, PETSC_ERR_FILE_READ, "Unable to read entire YAML file: %s", fname); 257 yamlString[yamlLength] = 0; 258 fclose(fd); 259 } 260 } 261 262 PetscCallMPI(MPI_Bcast(&yamlLength, 1, MPI_INT, 0, comm)); 263 PetscCheck(!require || yamlLength >= 0, comm, PETSC_ERR_FILE_OPEN, "Unable to open YAML option file: %s", file); 264 if (yamlLength < 0) PetscFunctionReturn(PETSC_SUCCESS); 265 266 if (rank) PetscCall(PetscMalloc1(yamlLength + 1, &yamlString)); 267 PetscCallMPI(MPI_Bcast(yamlString, yamlLength + 1, MPI_CHAR, 0, comm)); 268 269 prev = PetscYAMLSetComm(comm); 270 PetscCall(PetscOptionsInsertStringYAML_Private(options, yamlString, PETSC_OPT_FILE)); 271 (void)PetscYAMLSetComm(prev); 272 273 PetscCall(PetscFree(yamlString)); 274 PetscFunctionReturn(PETSC_SUCCESS); 275 } 276 277 #if !defined(PETSC_HAVE_YAML) 278 279 /* 280 #if !defined(PETSC_HAVE_STRDUP) 281 #define strdup(s) (char*)memcpy(malloc(strlen(s)+1),s,strlen(s)+1) 282 #endif 283 */ 284 285 /* Embed LibYAML in this compilation unit */ 286 #include <../src/sys/yaml/src/api.c> 287 #include <../src/sys/yaml/src/loader.c> 288 #include <../src/sys/yaml/src/parser.c> 289 #include <../src/sys/yaml/src/reader.c> 290 291 /* 292 Avoid compiler warnings like 293 scanner.c, line 3181: warning: integer conversion resulted in a change of sign 294 *(string.pointer++) = '\xC2'; 295 296 Once yaml fixes them, we can remove the pragmas 297 */ 298 #if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__) 299 #pragma GCC diagnostic push 300 #pragma GCC diagnostic ignored "-Wsign-conversion" 301 #endif 302 #include <../src/sys/yaml/src/scanner.c> 303 #if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__) 304 #pragma GCC diagnostic pop 305 #endif 306 307 /* Silence a few unused-function warnings */ 308 static PETSC_UNUSED void petsc_yaml_unused(void) 309 { 310 (void)yaml_parser_scan; 311 (void)yaml_document_get_node; 312 (void)yaml_parser_set_encoding; 313 (void)yaml_parser_set_input; 314 (void)yaml_parser_set_input_file; 315 } 316 317 #endif 318