Hand-written changes
diff --git a/tools/buildgen/build-cleaner.py b/tools/buildgen/build-cleaner.py
index fba1037..939143c 100755
--- a/tools/buildgen/build-cleaner.py
+++ b/tools/buildgen/build-cleaner.py
@@ -28,16 +28,16 @@
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
-# produces cleaner build.json files
+# produces cleaner build.yaml files
 
 import collections
-import json
 import os
 import sys
+import yaml
 
 TEST = (os.environ.get('TEST', 'false') == 'true')
 
-_TOP_LEVEL_KEYS = ['settings', 'filegroups', 'libs', 'targets']
+_TOP_LEVEL_KEYS = ['settings', 'filegroups', 'libs', 'targets', 'vspackages']
 _VERSION_KEYS = ['major', 'minor', 'micro', 'build']
 _ELEM_KEYS = [
     'name',
@@ -50,6 +50,11 @@
     'src',
     'deps']
 
+def repr_ordered_dict(dumper, odict):
+  return dumper.represent_mapping(u'tag:yaml.org,2002:map', odict.items())
+
+yaml.add_representer(collections.OrderedDict, repr_ordered_dict)
+
 def rebuild_as_ordered_dict(indict, special_keys):
   outdict = collections.OrderedDict()
   for key in sorted(indict.keys()):
@@ -75,7 +80,7 @@
 
 for filename in sys.argv[1:]:
   with open(filename) as f:
-    js = json.load(f)
+    js = yaml.load(f)
   js = rebuild_as_ordered_dict(js, _TOP_LEVEL_KEYS)
   js['settings']['version'] = rebuild_as_ordered_dict(
       js['settings']['version'], _VERSION_KEYS)
@@ -83,7 +88,7 @@
     if grp not in js: continue
     js[grp] = sorted([clean_elem(x) for x in js[grp]],
                      key=lambda x: (x.get('language', '_'), x['name']))
-  output = json.dumps(js, indent = 2)
+  output = yaml.dump(js, indent=2, width=80)
   # massage out trailing whitespace
   lines = []
   for line in output.splitlines():
diff --git a/tools/buildgen/generate_build_additions.sh b/tools/buildgen/generate_build_additions.sh
index 44fc255..b5df150 100644
--- a/tools/buildgen/generate_build_additions.sh
+++ b/tools/buildgen/generate_build_additions.sh
@@ -28,11 +28,11 @@
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
-gen_build_json_dirs="test/core/end2end test/core/bad_client"
+gen_build_yaml_dirs="test/core/end2end test/core/bad_client"
 gen_build_files=""
-for gen_build_json in $gen_build_json_dirs
+for gen_build_yaml in $gen_build_yaml_dirs
 do
   output_file=`mktemp /tmp/genXXXXXX`
-  $gen_build_json/gen_build_json.py > $output_file
+  $gen_build_yaml/gen_build_yaml.py > $output_file
   gen_build_files="$gen_build_files $output_file"
 done
diff --git a/tools/buildgen/generate_projects-old.sh b/tools/buildgen/generate_projects-old.sh
index 5399867..55d93d4 100644
--- a/tools/buildgen/generate_projects-old.sh
+++ b/tools/buildgen/generate_projects-old.sh
@@ -40,7 +40,7 @@
 mako_renderer=tools/buildgen/mako_renderer.py
 
 if [ "x$TEST" != "x" ] ; then
-  tools/buildgen/build-cleaner.py build.json
+  tools/buildgen/build-cleaner.py build.yaml
 fi
 
 . tools/buildgen/generate_build_additions.sh
@@ -58,8 +58,8 @@
     out=${dir}/${file#$dir/templates/}  # strip templates dir prefix
     out=${out%.*}  # strip template extension
     echo "generating file: $out"
-    json_files="build.json $gen_build_files"
-    data=`for i in $json_files ; do echo $i ; done | awk ' { printf "-d %s ", $0 } '`
+    yaml_files="build.yaml $gen_build_files"
+    data=`for i in $yaml_files ; do echo $i ; done | awk ' { printf "-d %s ", $0 } '`
     if [ "x$TEST" = "xtrue" ] ; then
       actual_out=$out
       out=`mktemp /tmp/gentXXXXXX`
diff --git a/tools/buildgen/generate_projects.sh b/tools/buildgen/generate_projects.sh
index 32fc90f..a887873 100755
--- a/tools/buildgen/generate_projects.sh
+++ b/tools/buildgen/generate_projects.sh
@@ -40,11 +40,11 @@
 mako_renderer=tools/buildgen/mako_renderer.py
 
 if [ "x$TEST" != "x" ] ; then
-  tools/buildgen/build-cleaner.py build.json
+  tools/buildgen/build-cleaner.py build.yaml
 fi
 
 . tools/buildgen/generate_build_additions.sh
 
-tools/buildgen/generate_projects.py build.json $gen_build_files
+tools/buildgen/generate_projects.py build.yaml $gen_build_files
 
 rm $gen_build_files
diff --git a/tools/buildgen/mako_renderer.py b/tools/buildgen/mako_renderer.py
index 534377e..08307bd 100755
--- a/tools/buildgen/mako_renderer.py
+++ b/tools/buildgen/mako_renderer.py
@@ -38,14 +38,15 @@
 import getopt
 import imp
 import os
+import shutil
 import sys
 
 
 from mako.lookup import TemplateLookup
 from mako.runtime import Context
 from mako.template import Template
-import simplejson
 import bunch
+import yaml
 
 
 # Imports a plugin
@@ -76,6 +77,7 @@
   got_output = False
   output_file = sys.stdout
   plugins = []
+  output_name = None
 
   try:
     opts, args = getopt.getopt(argv, 'hm:d:o:p:')
@@ -95,7 +97,7 @@
         showhelp()
         sys.exit(3)
       got_output = True
-      output_file = open(arg, 'w')
+      output_name = arg
     elif opt == '-m':
       if module_directory is not None:
         out('Got more than one cache directory')
@@ -104,7 +106,7 @@
       module_directory = arg
     elif opt == '-d':
       dict_file = open(arg, 'r')
-      bunch.merge_json(json_dict, simplejson.loads(dict_file.read()))
+      bunch.merge_json(json_dict, yaml.load(dict_file.read()))
       dict_file.close()
     elif opt == '-p':
       plugins.append(import_plugin(arg))
@@ -115,14 +117,51 @@
   for k, v in json_dict.items():
     dictionary[k] = bunch.to_bunch(v)
 
-  ctx = Context(output_file, **dictionary)
-
+  cleared_dir = False
   for arg in args:
     got_input = True
-    template = Template(filename=arg,
-                        module_directory=module_directory,
-                        lookup=TemplateLookup(directories=['.']))
-    template.render_context(ctx)
+    with open(arg) as f:
+      srcs = list(yaml.load_all(f.read()))
+    for src in srcs:
+      if isinstance(src, basestring):
+        assert len(srcs) == 1
+        template = Template(src,
+                            filename=arg,
+                            module_directory=module_directory,
+                            lookup=TemplateLookup(directories=['.']))
+        with open(output_name, 'w') as output_file:
+          template.render_context(Context(output_file, **dictionary))
+      else:
+        # we have optional control data: this template represents
+        # a directory
+        if not cleared_dir:
+          shutil.rmtree(output_name, ignore_errors=True)
+          cleared_dir = True
+        items = []
+        if 'foreach' in src:
+          for el in dictionary[src['foreach']]:
+            if 'cond' in src:
+              args = dict(dictionary)
+              args['selected'] = el
+              if not eval(src['cond'], {}, args):
+                continue
+            items.append(el)
+          assert items
+        else:
+          items = [None]
+        for item in items:
+          args = dict(dictionary)
+          args['selected'] = item
+          item_output_name = os.path.join(
+              output_name, Template(src['output_name']).render(**args))
+          if not os.path.exists(os.path.dirname(item_output_name)):
+            os.makedirs(os.path.dirname(item_output_name))
+          template = Template(src['template'],
+                              filename=arg,
+                              module_directory=module_directory,
+                              lookup=TemplateLookup(directories=['.']))
+          with open(item_output_name, 'w') as output_file:
+            template.render_context(Context(output_file, **args))
 
   if not got_input:
     out('Got nothing to do')
diff --git a/tools/buildgen/plugins/expand_bin_attrs.py b/tools/buildgen/plugins/expand_bin_attrs.py
index d221b3a..4f359e5 100755
--- a/tools/buildgen/plugins/expand_bin_attrs.py
+++ b/tools/buildgen/plugins/expand_bin_attrs.py
@@ -37,7 +37,7 @@
 def mako_plugin(dictionary):
   """The exported plugin code for expand_filegroups.
 
-  The list of libs in the build.json file can contain "filegroups" tags.
+  The list of libs in the build.yaml file can contain "filegroups" tags.
   These refer to the filegroups in the root object. We will expand and
   merge filegroups on the src, headers and public_headers properties.
 
diff --git a/tools/buildgen/plugins/expand_filegroups.py b/tools/buildgen/plugins/expand_filegroups.py
index f63072c..156bdc4 100755
--- a/tools/buildgen/plugins/expand_filegroups.py
+++ b/tools/buildgen/plugins/expand_filegroups.py
@@ -29,7 +29,7 @@
 
 """Buildgen expand filegroups plugin.
 
-This takes the list of libs from our json dictionary,
+This takes the list of libs from our yaml dictionary,
 and expands any and all filegroup.
 
 """
@@ -45,7 +45,7 @@
 def mako_plugin(dictionary):
   """The exported plugin code for expand_filegroups.
 
-  The list of libs in the build.json file can contain "filegroups" tags.
+  The list of libs in the build.yaml file can contain "filegroups" tags.
   These refer to the filegroups in the root object. We will expand and
   merge filegroups on the src, headers and public_headers properties.
 
diff --git a/tools/buildgen/plugins/generate_vsprojects.py b/tools/buildgen/plugins/generate_vsprojects.py
index 413056f..841a61f 100755
--- a/tools/buildgen/plugins/generate_vsprojects.py
+++ b/tools/buildgen/plugins/generate_vsprojects.py
@@ -57,14 +57,27 @@
   projects = []
   projects.extend(libs)
   projects.extend(targets)
-  if dictionary.get('debug', False):
-    for target in projects:
-      if not target.get('vs_project_guid', None) and 'windows' in target.get('platforms', ['windows']):
-        name = target['name']
-        guid = re.sub('(........)(....)(....)(....)(.*)',
-               r'{\1-\2-\3-\4-\5}',
-               hashlib.md5(name).hexdigest())
-        target['vs_project_guid'] = guid.upper()
+  for target in projects:
+    if 'build' in target and target['build'] == 'test':
+      default_test_dir = 'test'
+    else:
+      default_test_dir = '.'
+    if 'vs_config_type' not in target:
+      if 'build' in target and target['build'] == 'test':
+        target['vs_config_type'] = 'Application'
+      else:
+        target['vs_config_type'] = 'StaticLibrary'
+    if 'vs_packages' not in target:
+      target['vs_packages'] = []
+    if 'vs_props' not in target:
+      target['vs_props'] = []
+    target['vs_proj_dir'] = target.get('vs_proj_dir', default_test_dir)
+    if target.get('vs_project_guid', None) is None and 'windows' in target.get('platforms', ['windows']):
+      name = target['name']
+      guid = re.sub('(........)(....)(....)(....)(.*)',
+             r'{\1-\2-\3-\4-\5}',
+             hashlib.md5(name).hexdigest())
+      target['vs_project_guid'] = guid.upper()
   # Exclude projects without a visual project guid, such as the tests.
   projects = [project for project in projects
                 if project.get('vs_project_guid', None)]
@@ -74,5 +87,9 @@
 
   project_dict = dict([(p['name'], p) for p in projects])
 
+  packages = dictionary.get('vspackages', [])
+  packages_dict = dict([(p['name'], p) for p in packages])
+
   dictionary['vsprojects'] = projects
   dictionary['vsproject_dict'] = project_dict
+  dictionary['vspackages_dict'] = packages_dict
diff --git a/tools/buildgen/plugins/list_protos.py b/tools/buildgen/plugins/list_protos.py
index f12d726..2f2ac5e 100755
--- a/tools/buildgen/plugins/list_protos.py
+++ b/tools/buildgen/plugins/list_protos.py
@@ -29,7 +29,7 @@
 
 """Buildgen .proto files list plugin.
 
-This parses the list of targets from the json build file, and creates
+This parses the list of targets from the yaml build file, and creates
 a list called "protos" that contains all of the proto file names.
 
 """