Actual source code: optionsyaml.c

      1: #define PETSC_DESIRE_FEATURE_TEST_MACROS /* for strdup() */
      2: #include <petsc/private/petscimpl.h>

      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

     10: PETSC_INTERN PetscErrorCode PetscOptionsSetValue_Private(PetscOptions, const char[], const char[], int *, PetscOptionSource);
     11: PETSC_INTERN PetscErrorCode PetscOptionsInsertStringYAML_Private(PetscOptions, const char[], PetscOptionSource);

     13: static MPI_Comm petsc_yaml_comm = MPI_COMM_NULL; /* only used for parallel error handling */

     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: }

     20: static inline MPI_Comm PetscYAMLSetComm(MPI_Comm comm)
     21: {
     22:   MPI_Comm prev   = PetscYAMLGetComm();
     23:   petsc_yaml_comm = comm;
     24:   return prev;
     25: }

     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)

     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] = "";

     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;

     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");

     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:     }

     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:     }

     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));

     79:     } else if (valnode->type == YAML_SEQUENCE_NODE) {
     80:       PetscSegBuffer seg;
     81:       char          *buf, *strlist;
     82:       PetscBool      addSep = PETSC_FALSE;

     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;

     90:         PetscCheck(itemnode, comm, PETSC_ERR_LIB, "Corrupt YAML document");

     92:         if (itemnode->type == YAML_SCALAR_NODE) {
     93:           itemstr = STR(itemnode);

     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;

     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);

    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");

    108:             PetscCall(PetscStrcmp(STR(kn), "<<", &isMergeKey));
    109:             PetscCheck(!isMergeKey, comm, PETSC_ERR_SUP, "Unsupported YAML node value: merge key '<<' not supported here");

    111:             PetscCall(PetscStrbeginswith(STR(kn), "$$", &isDummyKey));
    112:             if (isDummyKey) continue;
    113:             itemstr = STR(kn);
    114:           }

    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));

    121:         } else SETERRQ(comm, PETSC_ERR_SUP, "Unsupported YAML node type: expected scalar or mapping");

    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));

    139:       PetscCall(PetscSNPrintf(name, sizeof(name), "-%s", STR(keynode)));
    140:       PetscCall(PetscOptionsSetValue_Private(options, name, strlist, NULL, source));
    141:       PetscCall(PetscFree(strlist));

    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));

    149:     } else SETERRQ(comm, PETSC_ERR_SUP, "Unsupported YAML node type: expected scalar, sequence or mapping");
    150:   }
    151:   PetscFunctionReturn(PETSC_SUCCESS);
    152: }

    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;

    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: }

    178: /*@
    179:   PetscOptionsInsertStringYAML - Inserts YAML-formatted options into the options database from a string

    181:   Logically Collective

    183:   Input Parameters:
    184: + options - options database, use `NULL` for default global database
    185: - in_str  - YAML-formatted string options

    187:   Level: intermediate

    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: }

    203: /*@
    204:   PetscOptionsInsertFileYAML - Insert a YAML-formatted file in the options database

    206:   Collective

    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

    214:   Level: intermediate

    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> .

    221:   Uses `PetscOptionsInsertStringYAML()`.

    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;

    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;

    245:     PetscCall(PetscStrreplace(PETSC_COMM_SELF, file, fpath, sizeof(fpath)));
    246:     PetscCall(PetscFixFilename(fpath, fname));

    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:   }

    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);

    266:   if (rank) PetscCall(PetscMalloc1(yamlLength + 1, &yamlString));
    267:   PetscCallMPI(MPI_Bcast(yamlString, yamlLength + 1, MPI_CHAR, 0, comm));

    269:   prev = PetscYAMLSetComm(comm);
    270:   PetscCall(PetscOptionsInsertStringYAML_Private(options, yamlString, PETSC_OPT_FILE));
    271:   (void)PetscYAMLSetComm(prev);

    273:   PetscCall(PetscFree(yamlString));
    274:   PetscFunctionReturn(PETSC_SUCCESS);
    275: }

    277: #if !defined(PETSC_HAVE_YAML)

    279:   /*
    280: #if !defined(PETSC_HAVE_STRDUP)
    281: #define strdup(s) (char*)memcpy(malloc(strlen(s)+1),s,strlen(s)+1)
    282: #endif
    283: */

    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>

    291: /*
    292:   Avoid compiler warnings like
    293:     scanner.c, line 3181: warning: integer conversion resulted in a change of sign
    294:                           *(string.pointer++) = '\xC2';

    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

    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: }

    317: #endif