Add option to the config_parser to list the files that comprise the
configuration.

The ultimate goal is to be able to generate automatically the dependency
list for the configuration build targets.

See CB-127

Change-Id: Ic8ea04003cb56e87d0c428d42e512deb6a93b3d9
diff --git a/config/config_parser/src/config_parser/config_reader.go b/config/config_parser/src/config_parser/config_reader.go
index 80d5538..439b66e 100644
--- a/config/config_parser/src/config_parser/config_reader.go
+++ b/config/config_parser/src/config_parser/config_reader.go
@@ -55,6 +55,30 @@
 	return c, nil
 }
 
+// GetConfigFilesListFromConfigDir reads the configuration for Cobalt from a
+// directory on the file system (See ReadConfigFromDir) and returns the list
+// of files which constitute the configuration. The purpose is generating a
+// list of dependencies.
+func GetConfigFilesListFromConfigDir(rootDir string) (files []string, err error) {
+	r, err := newConfigDirReader(rootDir)
+	if err != nil {
+		return files, err
+	}
+
+	l := []projectConfig{}
+	if err := readProjectsList(r, &l); err != nil {
+		return files, err
+	}
+
+	files = append(files, r.customersFilePath())
+
+	for i, _ := range l {
+		c := &(l[i])
+		files = append(files, r.projectFilePath(c.customerName, c.projectName))
+	}
+	return files, nil
+}
+
 // configReader is an interface that returns configuration data in the yaml format.
 type configReader interface {
 	// Returns the yaml representation of the customer and project list.
@@ -71,9 +95,9 @@
 	configDir string
 }
 
-// newConfigReaderForDir returns a configReader which will read the cobalt
-// configuration stored in the provided directory.
-func newConfigReaderForDir(configDir string) (r configReader, err error) {
+// newConfigDirReader returns a pointer to a configReader which will read the
+// Cobalt configuration stored in the provided directory.
+func newConfigDirReader(configDir string) (r *configDirReader, err error) {
 	info, err := os.Stat(configDir)
 	if err != nil {
 		return nil, err
@@ -86,26 +110,39 @@
 	return &configDirReader{configDir: configDir}, nil
 }
 
-func (r *configDirReader) Customers() (string, error) {
+// newConfigReaderForDir returns a configReader which will read the Cobalt
+// configuration stored in the provided directory.
+func newConfigReaderForDir(configDir string) (r configReader, err error) {
+	return newConfigDirReader(configDir)
+}
+
+func (r *configDirReader) customersFilePath() string {
 	// The customer and project list is at <rootDir>/projects.yaml
-	customerList, err := ioutil.ReadFile(filepath.Join(r.configDir, "projects.yaml"))
+	return filepath.Join(r.configDir, "projects.yaml")
+}
+
+func (r *configDirReader) Customers() (string, error) {
+	customerList, err := ioutil.ReadFile(r.customersFilePath())
 	if err != nil {
 		return "", err
 	}
 	return string(customerList), nil
 }
 
-func (r *configDirReader) Project(customerName string, projectName string) (string, error) {
+func (r *configDirReader) projectFilePath(customerName string, projectName string) string {
 	// A project's config is at <rootDir>/<customerName>/<projectName>/config.yaml
-	projectConfig, err := ioutil.ReadFile(filepath.Join(r.configDir, customerName, projectName, "config.yaml"))
+	return filepath.Join(r.configDir, customerName, projectName, "config.yaml")
+}
+
+func (r *configDirReader) Project(customerName string, projectName string) (string, error) {
+	projectConfig, err := ioutil.ReadFile(r.projectFilePath(customerName, projectName))
 	if err != nil {
 		return "", err
 	}
 	return string(projectConfig), nil
 }
 
-// readConfig reads and parses the configuration for all projects from a configReader.
-func readConfig(r configReader, l *[]projectConfig) (err error) {
+func readProjectsList(r configReader, l *[]projectConfig) (err error) {
 	// First, we get and parse the customer list.
 	customerListYaml, err := r.Customers()
 	if err != nil {
@@ -116,6 +153,15 @@
 		return err
 	}
 
+	return nil
+}
+
+// readConfig reads and parses the configuration for all projects from a configReader.
+func readConfig(r configReader, l *[]projectConfig) (err error) {
+	if err = readProjectsList(r, l); err != nil {
+		return err
+	}
+
 	// Then, based on the customer list, we read and parse all the project configs.
 	for i, _ := range *l {
 		c := &((*l)[i])
diff --git a/config/config_parser/src/config_parser_main.go b/config/config_parser/src/config_parser_main.go
index 4122d67..64a1b3f 100644
--- a/config/config_parser/src/config_parser_main.go
+++ b/config/config_parser/src/config_parser_main.go
@@ -22,18 +22,19 @@
 )
 
 var (
-	repoUrl        = flag.String("repo_url", "", "URL of the repository containing the config. Exactly one of 'repo_url', 'config_file' or 'config_dir' must be specified.")
-	configDir      = flag.String("config_dir", "", "Directory containing the config. Exactly one of 'repo_url', 'config_file' or 'config_dir' must be specified.")
-	configFile     = flag.String("config_file", "", "File containing the config for a single project. Exactly one of 'repo_url', 'config_file' or 'config_dir' must be specified.")
-	outFile        = flag.String("output_file", "", "File to which the serialized config should be written. Defaults to stdout.")
-	checkOnly      = flag.Bool("check_only", false, "Only check that the configuration is valid.")
-	skipValidation = flag.Bool("skip_validation", false, "Skip validating the config, write it no matter what.")
-	gitTimeoutSec  = flag.Int64("git_timeout", 60, "How many seconds should I wait on git commands?")
-	customerId     = flag.Int64("customer_id", -1, "Customer Id for the config to be read. Must be set if and only if 'config_file' is set.")
-	projectId      = flag.Int64("project_id", -1, "Project Id for the config to be read. Must be set if and only if 'config_file' is set.")
-	outFormat      = flag.String("out_format", "bin", "Specifies the output format. Supports 'bin' (serialized proto), 'b64' (serialized proto to base 64) and 'cpp' (ta C++ file containing a variable with a base64-encoded serialized proto.)")
-	varName        = flag.String("var_name", "config", "When using the 'cpp' output format, this will specify the variable name to be used in the output.")
-	namespace      = flag.String("namespace", "", "When using the 'cpp' output format, this will specify the comma-separated namespace within which the config variable must be places.")
+	repoUrl         = flag.String("repo_url", "", "URL of the repository containing the config. Exactly one of 'repo_url', 'config_file' or 'config_dir' must be specified.")
+	configDir       = flag.String("config_dir", "", "Directory containing the config. Exactly one of 'repo_url', 'config_file' or 'config_dir' must be specified.")
+	configFile      = flag.String("config_file", "", "File containing the config for a single project. Exactly one of 'repo_url', 'config_file' or 'config_dir' must be specified.")
+	outFile         = flag.String("output_file", "", "File to which the serialized config should be written. Defaults to stdout.")
+	checkOnly       = flag.Bool("check_only", false, "Only check that the configuration is valid.")
+	skipValidation  = flag.Bool("skip_validation", false, "Skip validating the config, write it no matter what.")
+	gitTimeoutSec   = flag.Int64("git_timeout", 60, "How many seconds should I wait on git commands?")
+	customerId      = flag.Int64("customer_id", -1, "Customer Id for the config to be read. Must be set if and only if 'config_file' is set.")
+	projectId       = flag.Int64("project_id", -1, "Project Id for the config to be read. Must be set if and only if 'config_file' is set.")
+	outFormat       = flag.String("out_format", "bin", "Specifies the output format. Supports 'bin' (serialized proto), 'b64' (serialized proto to base 64) and 'cpp' (ta C++ file containing a variable with a base64-encoded serialized proto.)")
+	varName         = flag.String("var_name", "config", "When using the 'cpp' output format, this will specify the variable name to be used in the output.")
+	namespace       = flag.String("namespace", "", "When using the 'cpp' output format, this will specify the comma-separated namespace within which the config variable must be places.")
+	listConfigFiles = flag.Bool("list_config_files", false, "List the files that comprise the configuration of Cobalt. This should be used in conjunction with the 'config_dir' flag only.")
 )
 
 func main() {
@@ -55,6 +56,10 @@
 		glog.Exit("'output_file' does not make sense if 'check_only' is set.")
 	}
 
+	if *listConfigFiles && *configDir == "" {
+		glog.Exit("'list_config_files' is only compatible with 'config_dir' being set.")
+	}
+
 	var configLocation string
 	if *repoUrl != "" {
 		configLocation = *repoUrl
@@ -64,6 +69,16 @@
 		configLocation = *configDir
 	}
 
+	if *listConfigFiles {
+		files, err := config_parser.GetConfigFilesListFromConfigDir(configLocation)
+		if err != nil {
+			glog.Exit(err)
+		}
+
+		fmt.Println(strings.Join(files, "\n"))
+		os.Exit(0)
+	}
+
 	var outputFormatter config_parser.OutputFormatter
 	switch *outFormat {
 	case "bin":