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: static MPI_Comm petsc_yaml_comm = MPI_COMM_NULL; /* only used for parallel error handling */

     12: PETSC_STATIC_INLINE MPI_Comm PetscYAMLGetComm(void)
     13: {
     14:   return PetscLikely(petsc_yaml_comm != MPI_COMM_NULL) ? petsc_yaml_comm : (petsc_yaml_comm = PETSC_COMM_SELF);
     15: }

     17: PETSC_STATIC_INLINE MPI_Comm PetscYAMLSetComm(MPI_Comm comm)
     18: {
     19:   MPI_Comm prev = PetscYAMLGetComm(); petsc_yaml_comm = comm; return prev;
     20: }

     22: #define TAG(node) ((const char *)((node)->tag))
     23: #define STR(node) ((const char *)((node)->data.scalar.value))
     24: #define SEQ(node) ((node)->data.sequence.items)
     25: #define MAP(node) ((node)->data.mapping.pairs)

     27: static PetscErrorCode PetscParseLayerYAML(PetscOptions options, yaml_document_t *doc, yaml_node_t *node)
     28: {
     29:   MPI_Comm         comm = PetscYAMLGetComm();
     30:   char             name[PETSC_MAX_OPTION_NAME] = "", prefix[PETSC_MAX_OPTION_NAME] = "";
     31:   PetscErrorCode   ierr;

     34:   if (node->type == YAML_SCALAR_NODE && !STR(node)[0]) return(0); /* empty */
     35:   if (node->type != YAML_MAPPING_NODE) SETERRQ(comm, PETSC_ERR_SUP, "Unsupported YAML node type: expected mapping");
     36:   for (yaml_node_pair_t *pair = MAP(node).start; pair < MAP(node).top; pair++) {
     37:     yaml_node_t *keynode = yaml_document_get_node(doc, pair->key);
     38:     yaml_node_t *valnode = yaml_document_get_node(doc, pair->value);
     39:     PetscBool   isMergeKey,isDummyKey,isIncludeTag;

     41:     if (!keynode) SETERRQ(comm, PETSC_ERR_LIB, "Corrupt YAML document");
     42:     if (!valnode) SETERRQ(comm, PETSC_ERR_LIB, "Corrupt YAML document");
     43:     if (keynode->type != YAML_SCALAR_NODE) SETERRQ(comm, PETSC_ERR_SUP, "Unsupported YAML node type: expected scalar");

     45:     /* "<<" is the merge key: don't increment the prefix */
     46:     PetscStrcmp(STR(keynode), "<<", &isMergeKey);
     47:     if (isMergeKey) {
     48:       if (valnode->type == YAML_SEQUENCE_NODE) {
     49:         for (yaml_node_item_t *item = SEQ(valnode).start; item < SEQ(valnode).top; item++) {
     50:           yaml_node_t *itemnode = yaml_document_get_node(doc, *item);
     51:           if (!itemnode) SETERRQ(comm, PETSC_ERR_LIB, "Corrupt YAML document");
     52:           if (itemnode->type != YAML_MAPPING_NODE) SETERRQ(comm, PETSC_ERR_SUP, "Unsupported YAML node type: expected mapping");
     53:           PetscParseLayerYAML(options, doc, itemnode);
     54:         }
     55:       } else if (valnode->type == YAML_MAPPING_NODE) {
     56:         PetscParseLayerYAML(options, doc, valnode);
     57:       } else SETERRQ(comm, PETSC_ERR_SUP, "Unsupported YAML node type: expected sequence or mapping");
     58:       continue; /* to next pair */
     59:     }

     61:     /* "$$*" are treated as dummy keys, we use them for !include tags and to define anchors */
     62:     PetscStrbeginswith(STR(keynode), "$$", &isDummyKey);
     63:     if (isDummyKey) {
     64:       PetscStrendswith(TAG(valnode), "!include", &isIncludeTag);
     65:       if (isIncludeTag) { /* TODO: add proper support relative paths */
     66:         PetscOptionsInsertFileYAML(comm, options, STR(valnode), PETSC_TRUE);
     67:       }
     68:       continue; /* to next pair */
     69:     }

     71:     if (valnode->type == YAML_SCALAR_NODE) {
     72:       PetscSNPrintf(name, sizeof(name), "-%s", STR(keynode));
     73:       PetscOptionsSetValue(options, name, STR(valnode));

     75:     } else if (valnode->type == YAML_SEQUENCE_NODE) {
     76:       PetscSegBuffer seg;
     77:       char           *buf, *strlist;
     78:       PetscBool      addSep = PETSC_FALSE;

     80:       PetscSegBufferCreate(sizeof(char), PETSC_MAX_PATH_LEN, &seg);
     81:       for (yaml_node_item_t *item = SEQ(valnode).start; item < SEQ(valnode).top; item++) {
     82:         yaml_node_t *itemnode = yaml_document_get_node(doc, *item);
     83:         const char  *itemstr = NULL;
     84:         size_t       itemlen;

     86:         if (!itemnode) SETERRQ(comm, PETSC_ERR_LIB, "Corrupt YAML document");

     88:         if (itemnode->type == YAML_SCALAR_NODE) {
     89:           itemstr = STR(itemnode);

     91:         } else if (itemnode->type == YAML_MAPPING_NODE) {
     92:           yaml_node_pair_t *kvn = itemnode->data.mapping.pairs.start;
     93:           yaml_node_pair_t *top = itemnode->data.mapping.pairs.top;

     95:           if (top - kvn > 1) SETERRQ(comm, PETSC_ERR_SUP, "Unsupported YAML node value: expected a single key:value pair");
     96:           if (top - kvn > 0) {
     97:             yaml_node_t *kn = yaml_document_get_node(doc, kvn->key);
     98:             yaml_node_t *vn = yaml_document_get_node(doc, kvn->value);

    100:             if (!kn) SETERRQ(comm, PETSC_ERR_LIB, "Corrupt YAML document");
    101:             if (!vn) SETERRQ(comm, PETSC_ERR_LIB, "Corrupt YAML document");
    102:             if (kn->type != YAML_SCALAR_NODE) SETERRQ(comm, PETSC_ERR_SUP, "Unsupported YAML node type: expected scalar");

    104:             PetscStrcmp(STR(kn), "<<", &isMergeKey);
    105:             if (isMergeKey) SETERRQ(comm, PETSC_ERR_SUP, "Unsupported YAML node value: merge key '<<' not supported here");

    107:             PetscStrbeginswith(STR(kn), "$$", &isDummyKey);
    108:             if (isDummyKey) continue;
    109:             itemstr = STR(kn);
    110:           }

    112:           PetscSNPrintf(prefix,sizeof(prefix), "%s_", STR(keynode));
    113:           PetscOptionsPrefixPush(options, prefix);
    114:           PetscParseLayerYAML(options, doc, itemnode);
    115:           PetscOptionsPrefixPop(options);

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

    119:         PetscStrlen(itemstr, &itemlen);
    120:         if (itemlen) {
    121:           if (addSep) {
    122:             PetscSegBufferGet(seg, 1, &buf);
    123:             PetscArraycpy(buf, ",", 1);
    124:           }
    125:           PetscSegBufferGet(seg, itemlen, &buf);
    126:           PetscArraycpy(buf, itemstr, itemlen);
    127:           addSep = PETSC_TRUE;
    128:         }
    129:       }
    130:       PetscSegBufferGet(seg, 1, &buf);
    131:       PetscArrayzero(buf, 1);
    132:       PetscSegBufferExtractAlloc(seg, &strlist);
    133:       PetscSegBufferDestroy(&seg);

    135:       PetscSNPrintf(name, sizeof(name), "-%s", STR(keynode));
    136:       PetscOptionsSetValue(options, name, strlist);
    137:       PetscFree(strlist);

    139:     } else if (valnode->type == YAML_MAPPING_NODE) {
    140:       PetscSNPrintf(prefix,sizeof(prefix), "%s_", STR(keynode));
    141:       PetscOptionsPrefixPush(options, prefix);
    142:       PetscParseLayerYAML(options, doc, valnode);
    143:       PetscOptionsPrefixPop(options);

    145:     } else SETERRQ(comm, PETSC_ERR_SUP, "Unsupported YAML node type: expected scalar, sequence or mapping");
    146:   }
    147:   return(0);
    148: }

    150: /*@C
    151:    PetscOptionsInsertStringYAML - Inserts YAML-formatted options into the database from a string

    153:    Logically Collective

    155:    Input Parameter:
    156: +  options - options database, use NULL for default global database
    157: -  in_str - YAML-formatted string options

    159:    Level: intermediate

    161: .seealso: PetscOptionsSetValue(), PetscOptionsView(), PetscOptionsHasName(), PetscOptionsGetInt(),
    162:           PetscOptionsGetReal(), PetscOptionsGetString(), PetscOptionsGetIntArray(), PetscOptionsBool(),
    163:           PetscOptionsName(), PetscOptionsBegin(), PetscOptionsEnd(), PetscOptionsHead(),
    164:           PetscOptionsStringArray(),PetscOptionsRealArray(), PetscOptionsScalar(),
    165:           PetscOptionsBoolGroupBegin(), PetscOptionsBoolGroup(), PetscOptionsBoolGroupEnd(),
    166:           PetscOptionsFList(), PetscOptionsEList(), PetscOptionsInsertFile(), PetscOptionsInsertFileYAML()
    167: @*/
    168: PetscErrorCode PetscOptionsInsertStringYAML(PetscOptions options,const char in_str[])
    169: {
    170:   MPI_Comm        comm = PetscYAMLGetComm();
    171:   yaml_parser_t   parser;
    172:   yaml_document_t doc;
    173:   yaml_node_t     *root;
    174:   PetscErrorCode  ierr;

    177:   if (!in_str) in_str = "";
    178:   !yaml_parser_initialize(&parser); if (ierr) SETERRQ(comm, PETSC_ERR_LIB, "YAML parser initialization error");
    179:   yaml_parser_set_input_string(&parser, (const unsigned char *)in_str, strlen(in_str));
    180:   do {
    181:     !yaml_parser_load(&parser, &doc); if (ierr) SETERRQ(comm, PETSC_ERR_LIB, "YAML parser loading error");
    182:     root = yaml_document_get_root_node(&doc);
    183:     if (root) {
    184:       PetscParseLayerYAML(options, &doc, root);
    185:     }
    186:     yaml_document_delete(&doc);
    187:   } while (root);
    188:   yaml_parser_delete(&parser);
    189:   return(0);
    190: }

    192: /*@C
    193:   PetscOptionsInsertFileYAML - Insert a YAML-formatted file in the options database

    195:   Collective

    197:   Input Parameter:
    198: +   comm - the processes that will share the options (usually PETSC_COMM_WORLD)
    199: .   options - options database, use NULL for default global database
    200: .   file - name of file
    201: -   require - if PETSC_TRUE will generate an error if the file does not exist

    203:   PETSc will generate an error condition that stops the program if a YAML error
    204:   is detected, hence the user should check that the YAML file is valid before
    205:   supplying it, for instance at http://www.yamllint.com/ .

    207:   Uses PetscOptionsInsertStringYAML().

    209:   Level: intermediate

    211: .seealso: PetscOptionsSetValue(), PetscOptionsView(), PetscOptionsHasName(), PetscOptionsGetInt(),
    212:           PetscOptionsGetReal(), PetscOptionsGetString(), PetscOptionsGetIntArray(), PetscOptionsBool(),
    213:           PetscOptionsName(), PetscOptionsBegin(), PetscOptionsEnd(), PetscOptionsHead(),
    214:           PetscOptionsStringArray(),PetscOptionsRealArray(), PetscOptionsScalar(),
    215:           PetscOptionsBoolGroupBegin(), PetscOptionsBoolGroup(), PetscOptionsBoolGroupEnd(),
    216:           PetscOptionsFList(), PetscOptionsEList(), PetscOptionsInsertFile(), PetscOptionsInsertStringYAML()
    217: @*/
    218: PetscErrorCode PetscOptionsInsertFileYAML(MPI_Comm comm,PetscOptions options,const char file[],PetscBool require)
    219: {
    220:   int            yamlLength = -1;
    221:   char          *yamlString = NULL;
    222:   MPI_Comm       prev;
    223:   PetscMPIInt    rank;

    227:   MPI_Comm_rank(comm, &rank);
    228:   if (!rank) {
    229:     char   fpath[PETSC_MAX_PATH_LEN];
    230:     char   fname[PETSC_MAX_PATH_LEN];
    231:     FILE  *fd;
    232:     size_t rd;

    234:     PetscStrreplace(PETSC_COMM_SELF, file, fpath, sizeof(fpath));
    235:     PetscFixFilename(fpath, fname);

    237:     fd = fopen(fname, "r");
    238:     if (fd) {
    239:       fseek(fd, 0, SEEK_END);
    240:       yamlLength = (int)ftell(fd);
    241:       fseek(fd, 0, SEEK_SET);
    242:       if (yamlLength < 0) SETERRQ1(PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN, "Unable to query size of YAML file: %s", fname);
    243:       PetscMalloc1(yamlLength+1, &yamlString);
    244:       rd = fread(yamlString, 1, (size_t)yamlLength, fd);
    245:       if (rd != (size_t)yamlLength) SETERRQ1(PETSC_COMM_SELF, PETSC_ERR_FILE_READ, "Unable to read entire YAML file: %s", fname);
    246:       yamlString[yamlLength] = 0;
    247:       fclose(fd);
    248:     }
    249:   }

    251:   MPI_Bcast(&yamlLength, 1, MPI_INT, 0, comm);
    252:   if (require && yamlLength < 0) SETERRQ1(comm, PETSC_ERR_FILE_OPEN, "Unable to open YAML option file: %s\n", file);
    253:   if (yamlLength < 0) return(0);

    255:   if (rank) {PetscMalloc1(yamlLength+1, &yamlString);}
    256:   MPI_Bcast(yamlString, yamlLength+1, MPI_CHAR, 0, comm);

    258:   prev = PetscYAMLSetComm(comm);
    259:   PetscOptionsInsertStringYAML(options, yamlString);
    260:   (void) PetscYAMLSetComm(prev);

    262:   PetscFree(yamlString);
    263:   return(0);
    264: }

    267: #if !defined(PETSC_HAVE_YAML)

    269: /*
    270: #if !defined(PETSC_HAVE_STRDUP)
    271: #define strdup(s) (char*)memcpy(malloc(strlen(s)+1),s,strlen(s)+1)
    272: #endif
    273: */

    275: /* Embed LibYAML in this compilation unit */
    276: #include <../src/sys/yaml/src/api.c>
    277: #include <../src/sys/yaml/src/loader.c>
    278: #include <../src/sys/yaml/src/parser.c>
    279: #include <../src/sys/yaml/src/reader.c>
    280: #include <../src/sys/yaml/src/scanner.c>

    282: /* Silence a few unused-function warnings */
    283: static PETSC_UNUSED void petsc_yaml_unused(void)
    284: {
    285:   (void)yaml_parser_scan;
    286:   (void)yaml_document_get_node;
    287:   (void)yaml_parser_set_encoding;
    288:   (void)yaml_parser_set_input;
    289:   (void)yaml_parser_set_input_file;
    290: }

    292: #endif